/*
 * UAE - The Un*x Amiga Emulator
 *
 * Memory management
 *
 * (c) 1995 Bernd Schmidt
 */

#include "sysconfig.h"
#include "ersatz.h"
#include "options.h"
#include "crc32.h"
#include "akiko.h"
#include "ar.h"
#include "cdtv.h"
#include "rommgr.h"
#include "savestate.h"
#include "debug.h"
#include "uae.h"
#include "gui.h"
#include "zfile.h"
#include "newcpu.h"
#include "a2091.h"
#include "filesys.h"
#include "hardfile.h"
#include "gayle.h"
#include "custom.h"
#include "win32.h"

// #define DEBUG_STUPID 1

// bool canbang;
// int candirect = -1;
#ifdef JIT
/* Set by each memory handler that does not simply access real memory. */
int special_mem;
#endif

#ifdef MEMSTAT
ULONGLONG MemStat::WordReads = 0;
ULONGLONG MemStat::LongReads = 0;
ULONGLONG MemStat::WordWrites = 0;
ULONGLONG MemStat::LongWrites = 0;
#endif

// static bool isdirectjit()
// {
//     return g_curr_conf.jit_cache_size && !g_curr_conf.comptrustbyte;
// }

// static bool canjit()
// {
//     if (g_curr_conf.cpu_model < 68020 && g_curr_conf.address_space_24)
//         return 0;
//     return 1;
// }

// static void nocanbang()
// {
//     canbang = 0;
// }

uint allocated_chipmem;
uint allocated_fastmem;
uint allocated_slowmem;
uint allocated_gfxmem;
uint allocated_z3fastmem /*, allocated_z3fastmem2, allocated_z3chipmem*/;
uint allocated_a3000lmem;
uint allocated_a3000hmem;
uint allocated_cardmem;
byte ce_banktype[65536];
byte ce_cachable[65536];

// #if defined (CPU_64_BIT)
// uint max_z3fastmem = 2048UL * 1024 * 1024;
// #else
uint max_z3fastmem = 512 * 1024 * 1024;
// #endif

static size_t bootrom_filepos, chip_filepos, slow_filepos, rom_filepos, a3000lmem_filepos, a3000hmem_filepos;

// /* Set if we notice during initialization that settings changed,
//    and we must clear all memory to prevent bogus contents from confusing
//    the Kickstart.  */
// static bool need_hardreset;

/* The address space setting used during the last reset.  */
static bool last_address_space_24;

addrbank* mem_banks[MEMORY_BANKS];

/* This has two functions. It either holds a host address that, when added
   to the 68k address, gives the host address corresponding to that 68k
   address (in which case the value in this array is even), OR it holds the
   same value as mem_banks, for those banks that have baseaddr==0. In that
   case, bit 0 is set (the memory access routines will take care of it).  */

byte* baseaddr[MEMORY_BANKS];

class MemArea
{
    public:

        CString name;
        UINT size;
        byte* address;

        MemArea ()
        {
            size = 0;
            address = nullptr;
        }

        ~MemArea ()
        {
            delete address;
        }
};

static CArray<MemArea*>mapped_memory;

#ifdef NO_INLINE_MEMORY_ACCESS
__inline uint longget(uae_ptr addr)
{
    return call_mem_get_func(get_mem_bank(addr).lget, addr);
}
__inline uint wordget(uae_ptr addr)
{
    return call_mem_get_func(get_mem_bank(addr).wget, addr);
}
__inline uint byteget(uae_ptr addr)
{
    return call_mem_get_func(get_mem_bank(addr).bget, addr);
}
__inline void longput(uae_ptr addr, uint l)
{
    call_mem_put_func(get_mem_bank(addr).lput, addr, l);
}
__inline void wordput(uae_ptr addr, uint w)
{
    call_mem_put_func(get_mem_bank(addr).wput, addr, w);
}
__inline void byteput(uae_ptr addr, uint b)
{
    call_mem_put_func(get_mem_bank(addr).bput, addr, b);
}
#endif

int addr_valid(TCHAR* txt, uae_ptr addr, uint len)
{
    addrbank* ab = &get_mem_bank(addr);
    if (ab == 0 || !(ab->flags & (ABFLAG_RAM | ABFLAG_ROM)) || addr < 0x100 || len < 0 || len > 16777215 || !valid_address(addr, len))
    {
        Logger::Write(L"corrupt %s pointer %x (%d) detected!\n", txt, addr, len);
        return 0;
    }
    return 1;
}

uint chipmem_mask, chipmem_full_mask, chipmem_full_size;
uint kickmem_mask, extendedkickmem_mask, extendedkickmem2_mask, slowmem_mask;
uint a3000lmem_mask, a3000hmem_mask, cardmem_mask;

static int illegal_count;
/* A dummy bank that only contains zeros */

static uint REGPARAM3 dummy_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 dummy_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 dummy_bget(uae_ptr) REGPARAM;
static void REGPARAM3 dummy_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 dummy_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 dummy_bput(uae_ptr, uint) REGPARAM;
static int REGPARAM3 dummy_check(uae_ptr addr, uint size) REGPARAM;

#define MAX_ILG 200
#define NONEXISTINGDATA 0
// #define NONEXISTINGDATA 0xffffffff

static void dummylog(int rw, uae_ptr addr, int size, uint val, int ins)
{
    if (illegal_count >= MAX_ILG && MAX_ILG > 0)
        return;
    /* ignore Zorro3 expansion space */
    if (addr >= 0xff000000 && addr <= 0xff000200)
        return;
    /* autoconfig and extended rom */
    if (addr >= 0xe00000 && addr <= 0xf7ffff)
        return;
    /* motherboard ram */
    if (addr >= 0x08000000 && addr <= 0x08000007)
        return;
    if (addr >= 0x07f00000 && addr <= 0x07f00007)
        return;
    if (addr >= 0x07f7fff0 && addr <= 0x07ffffff)
        return;
    if (MAX_ILG >= 0)
        illegal_count++;
    if (ins)
    {
        Logger::Write(L"WARNING: Illegal opcode %cget at %08lx PC=%x\n",
            size == 2 ? 'w' : 'l', addr, M68K_GETPC);
    }
    else if (rw)
    {
        Logger::Write(L"Illegal %cput at %08lx=%08lx PC=%x\n",
            size == 1 ? 'b' : size == 2 ? 'w' : 'l', addr, val, M68K_GETPC);
    }
    else
    {
        Logger::Write(L"Illegal %cget at %08lx PC=%x\n",
            size == 1 ? 'b' : size == 2 ? 'w' : 'l', addr, M68K_GETPC);
    }
}

static uint dummy_get(uae_ptr addr, int size)
{
    uint v;
    if (g_curr_conf.cpu_model >= 68020)
        return NONEXISTINGDATA;
    v = (regs.irc << 16) | regs.irc;
    if (size == 4)
    {
    }
    else if (size == 2)
    {
        v &= 0xffff;
    }
    else
    {
        v = (addr & 1) ? (v & 0xff) : ((v >> 8) & 0xff);
    }
    #if 0
    if (addr >= 0x10000000)
        Logger::Write(L"%08X %d = %08x\n", addr, size, v);
    #endif
    return v;
}

static uint REGPARAM2 dummy_lget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        dummylog(0, addr, 4, 0, 0);
    return dummy_get(addr, 4);
}
uint REGPARAM2 dummy_lgeti(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        dummylog(0, addr, 4, 0, 1);
    return dummy_get(addr, 4);
}

static uint REGPARAM2 dummy_wget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        dummylog(0, addr, 2, 0, 0);
    return dummy_get(addr, 2);
}
uint REGPARAM2 dummy_wgeti(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        dummylog(0, addr, 2, 0, 1);
    return dummy_get(addr, 2);
}

static uint REGPARAM2 dummy_bget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        dummylog(0, addr, 1, 0, 0);
    return dummy_get(addr, 1);
}

static void REGPARAM2 dummy_lput(uae_ptr addr, uint l)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        dummylog(1, addr, 4, l, 0);
}
static void REGPARAM2 dummy_wput(uae_ptr addr, uint w)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        dummylog(1, addr, 2, w, 0);
}
static void REGPARAM2 dummy_bput(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        dummylog(1, addr, 1, b, 0);
}

static int REGPARAM2 dummy_check(uae_ptr addr, uint size)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    return 0;
}

static void REGPARAM2 none_put(uae_ptr addr, uint v)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
}
static uint REGPARAM2 ones_get(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    return 0xffffffff;
}

/* Chip memory */

byte* chipmemory;

static int REGPARAM3 chipmem_check(uae_ptr addr, uint size) REGPARAM;
static byte* REGPARAM3 chipmem_xlate(uae_ptr addr) REGPARAM;

#ifdef AGA

/* AGA ce-chipram access */

static void ce2_timeout()
{
    wait_cpu_cycle_read(0, -1);
}

/*static*/ uint REGPARAM2 chipmem_lget_ce2(uae_ptr addr)
{
    uint* m;

    #ifdef JIT
    special_mem |= S_READ;
    #endif
    addr &= chipmem_mask;
    m = (uint*)(chipmemory + addr);
    ce2_timeout();
    return do_get_mem_long(m);
}

/*static*/ uint REGPARAM2 chipmem_wget_ce2(uae_ptr addr)
{
    ushort* m, v;

    #ifdef JIT
    special_mem |= S_READ;
    #endif
    addr &= chipmem_mask;
    m = (ushort*)(chipmemory + addr);
    ce2_timeout();
    v = do_get_mem_word(m);
    return v;
}

/*static*/ uint REGPARAM2 chipmem_bget_ce2(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    addr &= chipmem_mask;
    ce2_timeout();
    return chipmemory[addr];
}

/*static*/ void REGPARAM2 chipmem_lput_ce2(uae_ptr addr, uint l)
{
    uint* m;

    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    addr &= chipmem_mask;
    m = (uint*)(chipmemory + addr);
    ce2_timeout();
    do_put_mem_long(m, l);
}

/*static*/ void REGPARAM2 chipmem_wput_ce2(uae_ptr addr, uint w)
{
    ushort* m;

    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    addr &= chipmem_mask;
    m = (ushort*)(chipmemory + addr);
    ce2_timeout();
    do_put_mem_word(m, w);
}

static void REGPARAM2 chipmem_bput_ce2(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    addr &= chipmem_mask;
    ce2_timeout();
    chipmemory[addr] = b;
}

#endif

uint REGPARAM2 chipmem_lget(uae_ptr addr)
{
    uint* m;

    addr &= chipmem_mask;
    m = (uint*)(chipmemory + addr);
    return do_get_mem_long(m);
}

/*static*/ uint REGPARAM2 chipmem_wget(uae_ptr addr)
{
    ushort* m, v;

    addr &= chipmem_mask;
    m = (ushort*)(chipmemory + addr);
    v = do_get_mem_word(m);
    return v;
}

/*static*/ uint REGPARAM2 chipmem_bget(uae_ptr addr)
{
    byte v;
    addr &= chipmem_mask;
    v = chipmemory[addr];
    return v;
}

void REGPARAM2 chipmem_lput(uae_ptr addr, uint l)
{
    uint* m;

    addr &= chipmem_mask;
    m = (uint*)(chipmemory + addr);
    do_put_mem_long(m, l);
}

void REGPARAM2 chipmem_wput(uae_ptr addr, uint w)
{
    ushort* m;

    addr &= chipmem_mask;
    m = (ushort*)(chipmemory + addr);
    do_put_mem_word(m, w);
}

void REGPARAM2 chipmem_bput(uae_ptr addr, uint b)
{
    addr &= chipmem_mask;
    chipmemory[addr] = b;
}

/* cpu chipmem access inside agnus addressable ram but no ram available */
static uint chipmem_dummy()
{
    /* not really right but something random that has more ones than zeros.. */
    return 0xffff & ~((1 << (rand() & 31)) | (1 << (rand() & 31)));
}
void REGPARAM2 chipmem_dummy_bput(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
}
void REGPARAM2 chipmem_dummy_wput(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
}
void REGPARAM2 chipmem_dummy_lput(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
}
static uint REGPARAM2 chipmem_dummy_bget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    return chipmem_dummy();
}
static uint REGPARAM2 chipmem_dummy_wget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    return chipmem_dummy();
}
static uint REGPARAM2 chipmem_dummy_lget(uae_ptr addr)
{
    #ifdef JIT
    special_mem |= S_READ;
    #endif
    return (chipmem_dummy() << 16) | chipmem_dummy();
}

/*static*/ uint REGPARAM2 chipmem_agnus_lget(uae_ptr addr)
{
    uint* m;

    addr &= chipmem_full_mask;
    m = (uint*)(chipmemory + addr);
    return do_get_mem_long(m);
}

uint REGPARAM2 chipmem_agnus_wget(uae_ptr addr)
{
    ushort* m;

    addr &= chipmem_full_mask;
    m = (ushort*)(chipmemory + addr);
    return do_get_mem_word(m);
}

/*static*/ uint REGPARAM2 chipmem_agnus_bget(uae_ptr addr)
{
    addr &= chipmem_full_mask;
    return chipmemory[addr];
}

/*static*/ void REGPARAM2 chipmem_agnus_lput(uae_ptr addr, uint l)
{
    uint* m;

    addr &= chipmem_full_mask;
    if (addr >= chipmem_full_size)
        return;
    m = (uint*)(chipmemory + addr);
    do_put_mem_long(m, l);
}

void REGPARAM2 chipmem_agnus_wput(uae_ptr addr, uint w)
{
    ushort* m;

    addr &= chipmem_full_mask;
    if (addr >= chipmem_full_size)
        return;
    m = (ushort*)(chipmemory + addr);
    do_put_mem_word(m, w);
}

/*static*/ void REGPARAM2 chipmem_agnus_bput(uae_ptr addr, uint b)
{
    addr &= chipmem_full_mask;
    if (addr >= chipmem_full_size)
        return;
    chipmemory[addr] = b;
}

static int REGPARAM2 chipmem_check(uae_ptr addr, uint size)
{
    addr &= chipmem_mask;
    return (addr + size) <= chipmem_full_size;
}

static byte* REGPARAM2 chipmem_xlate(uae_ptr addr)
{
    addr &= chipmem_mask;
    return chipmemory + addr;
}

// static __forceinline void REGPARAM2 chipmem_lput_bigmem(uae_ptr addr, uint v)
// {
//     put_long(addr, v);
// }
// static __forceinline void REGPARAM2 chipmem_wput_bigmem(uae_ptr addr, uint v)
// {
//     put_word(addr, v);
// }
// static __forceinline void REGPARAM2 chipmem_bput_bigmem(uae_ptr addr, uint v)
// {
//     put_byte(addr, v);
// }
// static __forceinline uint REGPARAM2 chipmem_lget_bigmem(uae_ptr addr)
// {
//     return get_long(addr);
// }
// static __forceinline uint REGPARAM2 chipmem_wget_bigmem(uae_ptr addr)
// {
//     return get_word(addr);
// }
// static __forceinline uint REGPARAM2 chipmem_bget_bigmem(uae_ptr addr)
// {
//     return get_byte(addr);
// }
// static __forceinline int REGPARAM2 chipmem_check_bigmem(uae_ptr addr, uint size)
// {
//     return valid_address(addr, size);
// }
// static __forceinline byte* REGPARAM2 chipmem_xlate_bigmem(uae_ptr addr)
// {
//     return get_real_address(addr);
// }

uint (REGPARAM2* chipmem_lget_indirect)(uae_ptr);
uint (REGPARAM2* chipmem_wget_indirect)(uae_ptr);
uint (REGPARAM2* chipmem_bget_indirect)(uae_ptr);
void (REGPARAM2* chipmem_lput_indirect)(uae_ptr, uint);
void (REGPARAM2* chipmem_wput_indirect)(uae_ptr, uint);
void (REGPARAM2* chipmem_bput_indirect)(uae_ptr, uint);
int (REGPARAM2* chipmem_check_indirect)(uae_ptr, uint);
byte*(REGPARAM2 * chipmem_xlate_indirect)(uae_ptr);

static void chipmem_setindirect()
{
    // if (g_curr_conf.z3chipmem_size)
    // {
    //     chipmem_lget_indirect = chipmem_lget_bigmem;
    //     chipmem_wget_indirect = chipmem_wget_bigmem;
    //     chipmem_bget_indirect = chipmem_bget_bigmem;
    //     chipmem_lput_indirect = chipmem_lput_bigmem;
    //     chipmem_wput_indirect = chipmem_wput_bigmem;
    //     chipmem_bput_indirect = chipmem_bput_bigmem;
    //     chipmem_check_indirect = chipmem_check_bigmem;
    //     chipmem_xlate_indirect = chipmem_xlate_bigmem;
    // }
    // else
    // {
    chipmem_lget_indirect = chipmem_lget;
    chipmem_wget_indirect = chipmem_agnus_wget;
    chipmem_bget_indirect = chipmem_agnus_bget;
    chipmem_lput_indirect = chipmem_lput;
    chipmem_wput_indirect = chipmem_agnus_wput;
    chipmem_bput_indirect = chipmem_agnus_bput;
    chipmem_check_indirect = chipmem_check;
    chipmem_xlate_indirect = chipmem_xlate;
    // }
}

/* Slow memory */

static byte* slowmemory;
bool slowmemory_allocated = false;

static uint REGPARAM3 slowmem_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 slowmem_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 slowmem_bget(uae_ptr) REGPARAM;
static void REGPARAM3 slowmem_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 slowmem_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 slowmem_bput(uae_ptr, uint) REGPARAM;
static int REGPARAM3 slowmem_check(uae_ptr addr, uint size) REGPARAM;
static byte* REGPARAM3 slowmem_xlate(uae_ptr addr) REGPARAM;

static uint REGPARAM2 slowmem_lget(uae_ptr addr)
{
    uint* m;
    addr &= slowmem_mask;
    m = (uint*)(slowmemory + addr);
    return do_get_mem_long(m);
}

static uint REGPARAM2 slowmem_wget(uae_ptr addr)
{
    ushort* m;
    addr &= slowmem_mask;
    m = (ushort*)(slowmemory + addr);
    return do_get_mem_word(m);
}

static uint REGPARAM2 slowmem_bget(uae_ptr addr)
{
    addr &= slowmem_mask;
    return slowmemory[addr];
}

static void REGPARAM2 slowmem_lput(uae_ptr addr, uint l)
{
    uint* m;
    addr &= slowmem_mask;
    m = (uint*)(slowmemory + addr);
    do_put_mem_long(m, l);
}

static void REGPARAM2 slowmem_wput(uae_ptr addr, uint w)
{
    ushort* m;
    addr &= slowmem_mask;
    m = (ushort*)(slowmemory + addr);
    do_put_mem_word(m, w);
}

static void REGPARAM2 slowmem_bput(uae_ptr addr, uint b)
{
    addr &= slowmem_mask;
    slowmemory[addr] = b;
}

static int REGPARAM2 slowmem_check(uae_ptr addr, uint size)
{
    addr &= slowmem_mask;
    return (addr + size) <= allocated_slowmem;
}

static byte* REGPARAM2 slowmem_xlate(uae_ptr addr)
{
    addr &= slowmem_mask;
    return slowmemory + addr;
}

/* CDTV expension memory card memory */

byte* cardmemory;

static uint REGPARAM3 cardmem_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 cardmem_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 cardmem_bget(uae_ptr) REGPARAM;
static void REGPARAM3 cardmem_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 cardmem_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 cardmem_bput(uae_ptr, uint) REGPARAM;
static int REGPARAM3 cardmem_check(uae_ptr addr, uint size) REGPARAM;
static byte* REGPARAM3 cardmem_xlate(uae_ptr addr) REGPARAM;

static uint REGPARAM2 cardmem_lget(uae_ptr addr)
{
    uint* m;
    addr &= cardmem_mask;
    m = (uint*)(cardmemory + addr);
    return do_get_mem_long(m);
}

static uint REGPARAM2 cardmem_wget(uae_ptr addr)
{
    ushort* m;
    addr &= cardmem_mask;
    m = (ushort*)(cardmemory + addr);
    return do_get_mem_word(m);
}

static uint REGPARAM2 cardmem_bget(uae_ptr addr)
{
    addr &= cardmem_mask;
    return cardmemory[addr];
}

static void REGPARAM2 cardmem_lput(uae_ptr addr, uint l)
{
    uint* m;
    addr &= cardmem_mask;
    m = (uint*)(cardmemory + addr);
    do_put_mem_long(m, l);
}

static void REGPARAM2 cardmem_wput(uae_ptr addr, uint w)
{
    ushort* m;
    addr &= cardmem_mask;
    m = (ushort*)(cardmemory + addr);
    do_put_mem_word(m, w);
}

static void REGPARAM2 cardmem_bput(uae_ptr addr, uint b)
{
    addr &= cardmem_mask;
    cardmemory[addr] = b;
}

static int REGPARAM2 cardmem_check(uae_ptr addr, uint size)
{
    addr &= cardmem_mask;
    return (addr + size) <= allocated_cardmem;
}

static byte* REGPARAM2 cardmem_xlate(uae_ptr addr)
{
    addr &= cardmem_mask;
    return cardmemory + addr;
}

/* A3000 motherboard fast memory */
static byte* a3000lmemory, * a3000hmemory;
uint a3000lmem_start, a3000hmem_start;

static uint REGPARAM3 a3000lmem_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 a3000lmem_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 a3000lmem_bget(uae_ptr) REGPARAM;
static void REGPARAM3 a3000lmem_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 a3000lmem_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 a3000lmem_bput(uae_ptr, uint) REGPARAM;
static int REGPARAM3 a3000lmem_check(uae_ptr addr, uint size) REGPARAM;
static byte* REGPARAM3 a3000lmem_xlate(uae_ptr addr) REGPARAM;

static uint REGPARAM2 a3000lmem_lget(uae_ptr addr)
{
    uint* m;
    addr &= a3000lmem_mask;
    m = (uint*)(a3000lmemory + addr);
    return do_get_mem_long(m);
}

static uint REGPARAM2 a3000lmem_wget(uae_ptr addr)
{
    ushort* m;
    addr &= a3000lmem_mask;
    m = (ushort*)(a3000lmemory + addr);
    return do_get_mem_word(m);
}

static uint REGPARAM2 a3000lmem_bget(uae_ptr addr)
{
    addr &= a3000lmem_mask;
    return a3000lmemory[addr];
}

static void REGPARAM2 a3000lmem_lput(uae_ptr addr, uint l)
{
    uint* m;
    addr &= a3000lmem_mask;
    m = (uint*)(a3000lmemory + addr);
    do_put_mem_long(m, l);
}

static void REGPARAM2 a3000lmem_wput(uae_ptr addr, uint w)
{
    ushort* m;
    addr &= a3000lmem_mask;
    m = (ushort*)(a3000lmemory + addr);
    do_put_mem_word(m, w);
}

static void REGPARAM2 a3000lmem_bput(uae_ptr addr, uint b)
{
    addr &= a3000lmem_mask;
    a3000lmemory[addr] = b;
}

static int REGPARAM2 a3000lmem_check(uae_ptr addr, uint size)
{
    addr &= a3000lmem_mask;
    return (addr + size) <= allocated_a3000lmem;
}

static byte* REGPARAM2 a3000lmem_xlate(uae_ptr addr)
{
    addr &= a3000lmem_mask;
    return a3000lmemory + addr;
}

static uint REGPARAM3 a3000hmem_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 a3000hmem_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 a3000hmem_bget(uae_ptr) REGPARAM;
static void REGPARAM3 a3000hmem_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 a3000hmem_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 a3000hmem_bput(uae_ptr, uint) REGPARAM;
static int REGPARAM3 a3000hmem_check(uae_ptr addr, uint size) REGPARAM;
static byte* REGPARAM3 a3000hmem_xlate(uae_ptr addr) REGPARAM;

static uint REGPARAM2 a3000hmem_lget(uae_ptr addr)
{
    uint* m;
    addr &= a3000hmem_mask;
    m = (uint*)(a3000hmemory + addr);
    return do_get_mem_long(m);
}

static uint REGPARAM2 a3000hmem_wget(uae_ptr addr)
{
    ushort* m;
    addr &= a3000hmem_mask;
    m = (ushort*)(a3000hmemory + addr);
    return do_get_mem_word(m);
}

static uint REGPARAM2 a3000hmem_bget(uae_ptr addr)
{
    addr &= a3000hmem_mask;
    return a3000hmemory[addr];
}

static void REGPARAM2 a3000hmem_lput(uae_ptr addr, uint l)
{
    uint* m;
    addr &= a3000hmem_mask;
    m = (uint*)(a3000hmemory + addr);
    do_put_mem_long(m, l);
}

static void REGPARAM2 a3000hmem_wput(uae_ptr addr, uint w)
{
    ushort* m;
    addr &= a3000hmem_mask;
    m = (ushort*)(a3000hmemory + addr);
    do_put_mem_word(m, w);
}

static void REGPARAM2 a3000hmem_bput(uae_ptr addr, uint b)
{
    addr &= a3000hmem_mask;
    a3000hmemory[addr] = b;
}

static int REGPARAM2 a3000hmem_check(uae_ptr addr, uint size)
{
    addr &= a3000hmem_mask;
    return (addr + size) <= allocated_a3000hmem;
}

static byte* REGPARAM2 a3000hmem_xlate(uae_ptr addr)
{
    addr &= a3000hmem_mask;
    return a3000hmemory + addr;
}

/* Kick memory */

byte* kickmemory;
ushort kickstart_version;
/*static*/ int kickmem_size;

/*
 * A1000 kickstart RAM handling
 *
 * RESET instruction unhides boot ROM and disables write protection
 * write access to boot ROM hides boot ROM and enables write protection
 *
 */
static int a1000_kickstart_mode;
static byte* a1000_bootrom;
static void a1000_handle_kickstart(int mode)
{
    if (!a1000_bootrom)
        return;
    if (mode == 0)
    {
        a1000_kickstart_mode = 0;
        memcpy(kickmemory, kickmemory + 262144, 262144);
        kickstart_version = (kickmemory[262144 + 12] << 8) | kickmemory[262144 + 13];
    }
    else
    {
        a1000_kickstart_mode = 1;
        memcpy(kickmemory, a1000_bootrom, 262144);
        kickstart_version = 0;
    }
    if (kickstart_version == 0xffff)
        kickstart_version = 0;
}

void a1000_reset()
{
    a1000_handle_kickstart(1);
}

static uint REGPARAM3 kickmem_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 kickmem_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 kickmem_bget(uae_ptr) REGPARAM;
static void REGPARAM3 kickmem_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 kickmem_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 kickmem_bput(uae_ptr, uint) REGPARAM;
static int REGPARAM3 kickmem_check(uae_ptr addr, uint size) REGPARAM;
static byte* REGPARAM3 kickmem_xlate(uae_ptr addr) REGPARAM;

static uint REGPARAM2 kickmem_lget(uae_ptr addr)
{
    uint* m;
    addr &= kickmem_mask;
    m = (uint*)(kickmemory + addr);
    return do_get_mem_long(m);
}

static uint REGPARAM2 kickmem_wget(uae_ptr addr)
{
    ushort* m;
    addr &= kickmem_mask;
    m = (ushort*)(kickmemory + addr);
    return do_get_mem_word(m);
}

static uint REGPARAM2 kickmem_bget(uae_ptr addr)
{
    addr &= kickmem_mask;
    return kickmemory[addr];
}

static void REGPARAM2 kickmem_lput(uae_ptr addr, uint b)
{
    uint* m;
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (a1000_kickstart_mode)
    {
        if (addr >= 0xfc0000)
        {
            addr &= kickmem_mask;
            m = (uint*)(kickmemory + addr);
            do_put_mem_long(m, b);
            return;
        }
        else
            a1000_handle_kickstart(0);
    }
    else if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        Logger::Write(L"Illegal kickmem lput at %08lx\n", addr);
}

static void REGPARAM2 kickmem_wput(uae_ptr addr, uint b)
{
    ushort* m;
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (a1000_kickstart_mode)
    {
        if (addr >= 0xfc0000)
        {
            addr &= kickmem_mask;
            m = (ushort*)(kickmemory + addr);
            do_put_mem_word(m, b);
            return;
        }
        else
            a1000_handle_kickstart(0);
    }
    else if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        Logger::Write(L"Illegal kickmem wput at %08lx\n", addr);
}

static void REGPARAM2 kickmem_bput(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (a1000_kickstart_mode)
    {
        if (addr >= 0xfc0000)
        {
            addr &= kickmem_mask;
            kickmemory[addr] = b;
            return;
        }
        else
            a1000_handle_kickstart(0);
    }
    else if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        Logger::Write(L"Illegal kickmem bput at %08lx\n", addr);
}

static void REGPARAM2 kickmem2_lput(uae_ptr addr, uint l)
{
    uint* m;
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    addr &= kickmem_mask;
    m = (uint*)(kickmemory + addr);
    do_put_mem_long(m, l);
}

static void REGPARAM2 kickmem2_wput(uae_ptr addr, uint w)
{
    ushort* m;
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    addr &= kickmem_mask;
    m = (ushort*)(kickmemory + addr);
    do_put_mem_word(m, w);
}

static void REGPARAM2 kickmem2_bput(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    addr &= kickmem_mask;
    kickmemory[addr] = b;
}

static int REGPARAM2 kickmem_check(uae_ptr addr, uint size)
{
    addr &= kickmem_mask;
    return (addr + size) <= kickmem_size;
}

static byte* REGPARAM2 kickmem_xlate(uae_ptr addr)
{
    addr &= kickmem_mask;
    return kickmemory + addr;
}

/* CD32/CDTV extended kick memory */

byte* extendedkickmemory, * extendedkickmemory2;
static int extendedkickmem_size, extendedkickmem2_size;
static uint extendedkickmem_start, extendedkickmem2_start;
static int extendedkickmem_type;

#define EXTENDED_ROM_CD32 1
#define EXTENDED_ROM_CDTV 2
#define EXTENDED_ROM_KS 3
// #define EXTENDED_ROM_ARCADIA 4

static uint REGPARAM3 extendedkickmem_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 extendedkickmem_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 extendedkickmem_bget(uae_ptr) REGPARAM;
static void REGPARAM3 extendedkickmem_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 extendedkickmem_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 extendedkickmem_bput(uae_ptr, uint) REGPARAM;
static int REGPARAM3 extendedkickmem_check(uae_ptr addr, uint size) REGPARAM;
static byte* REGPARAM3 extendedkickmem_xlate(uae_ptr addr) REGPARAM;
static uint REGPARAM2 extendedkickmem_lget(uae_ptr addr)
{
    uint* m;
    addr -= extendedkickmem_start & extendedkickmem_mask;
    addr &= extendedkickmem_mask;
    m = (uint*)(extendedkickmemory + addr);
    return do_get_mem_long(m);
}
static uint REGPARAM2 extendedkickmem_wget(uae_ptr addr)
{
    ushort* m;
    addr -= extendedkickmem_start & extendedkickmem_mask;
    addr &= extendedkickmem_mask;
    m = (ushort*)(extendedkickmemory + addr);
    return do_get_mem_word(m);
}
static uint REGPARAM2 extendedkickmem_bget(uae_ptr addr)
{
    addr -= extendedkickmem_start & extendedkickmem_mask;
    addr &= extendedkickmem_mask;
    return extendedkickmemory[addr];
}
static void REGPARAM2 extendedkickmem_lput(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        Logger::Write(L"Illegal extendedkickmem lput at %08lx\n", addr);
}
static void REGPARAM2 extendedkickmem_wput(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        Logger::Write(L"Illegal extendedkickmem wput at %08lx\n", addr);
}
static void REGPARAM2 extendedkickmem_bput(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        Logger::Write(L"Illegal extendedkickmem lput at %08lx\n", addr);
}
static int REGPARAM2 extendedkickmem_check(uae_ptr addr, uint size)
{
    addr -= extendedkickmem_start & extendedkickmem_mask;
    addr &= extendedkickmem_mask;
    return (addr + size) <= extendedkickmem_size;
}
static byte* REGPARAM2 extendedkickmem_xlate(uae_ptr addr)
{
    addr -= extendedkickmem_start & extendedkickmem_mask;
    addr &= extendedkickmem_mask;
    return extendedkickmemory + addr;
}

static uint REGPARAM3 extendedkickmem2_lget(uae_ptr) REGPARAM;
static uint REGPARAM3 extendedkickmem2_wget(uae_ptr) REGPARAM;
static uint REGPARAM3 extendedkickmem2_bget(uae_ptr) REGPARAM;
static void REGPARAM3 extendedkickmem2_lput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 extendedkickmem2_wput(uae_ptr, uint) REGPARAM;
static void REGPARAM3 extendedkickmem2_bput(uae_ptr, uint) REGPARAM;
static int REGPARAM3 extendedkickmem2_check(uae_ptr addr, uint size) REGPARAM;
static byte* REGPARAM3 extendedkickmem2_xlate(uae_ptr addr) REGPARAM;
static uint REGPARAM2 extendedkickmem2_lget(uae_ptr addr)
{
    uint* m;
    addr -= extendedkickmem2_start & extendedkickmem2_mask;
    addr &= extendedkickmem2_mask;
    m = (uint*)(extendedkickmemory2 + addr);
    return do_get_mem_long(m);
}
static uint REGPARAM2 extendedkickmem2_wget(uae_ptr addr)
{
    ushort* m;
    addr -= extendedkickmem2_start & extendedkickmem2_mask;
    addr &= extendedkickmem2_mask;
    m = (ushort*)(extendedkickmemory2 + addr);
    return do_get_mem_word(m);
}
static uint REGPARAM2 extendedkickmem2_bget(uae_ptr addr)
{
    addr -= extendedkickmem2_start & extendedkickmem2_mask;
    addr &= extendedkickmem2_mask;
    return extendedkickmemory2[addr];
}
static void REGPARAM2 extendedkickmem2_lput(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        Logger::Write(L"Illegal extendedkickmem2 lput at %08lx\n", addr);
}
static void REGPARAM2 extendedkickmem2_wput(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        Logger::Write(L"Illegal extendedkickmem2 wput at %08lx\n", addr);
}
static void REGPARAM2 extendedkickmem2_bput(uae_ptr addr, uint b)
{
    #ifdef JIT
    special_mem |= S_WRITE;
    #endif
    if (Logging::MEMORY_ILLEGAL_ACCSESS_DEBUG)
        Logger::Write(L"Illegal extendedkickmem2 lput at %08lx\n", addr);
}
static int REGPARAM2 extendedkickmem2_check(uae_ptr addr, uint size)
{
    addr -= extendedkickmem2_start & extendedkickmem2_mask;
    addr &= extendedkickmem2_mask;
    return (addr + size) <= extendedkickmem2_size;
}
static byte* REGPARAM2 extendedkickmem2_xlate(uae_ptr addr)
{
    addr -= extendedkickmem2_start & extendedkickmem2_mask;
    addr &= extendedkickmem2_mask;
    return extendedkickmemory2 + addr;
}

/* Default memory access functions */

int REGPARAM2 default_check(uae_ptr a, uint b)
{
    return 0;
}

static int be_cnt;

byte* REGPARAM2 default_xlate(uae_ptr a)
{
    if (quit_program == 0)
    {
        /* do this only in 68010+ mode, there are some tricky A500 programs.. */
        if ((g_curr_conf.cpu_model > 68000 || !g_curr_conf.cpu_compatible) && !g_curr_conf.mmu_model)
        {
            // /*#if defined (ENFORCER)
            // enforcer_disable();
            // #endif*/

            if (be_cnt < 3)
            {
                int i, j;
                uae_ptr a2 = a - 32;
                uae_ptr a3 = m68k_getpc() - 32;
                Logger::Write(L"Your Amiga program just did something terribly stupid %08X PC=%08X\n", a, M68K_GETPC);
                #if DEBUG_STUPID
                activate_debugger();
                #endif
                m68k_dumpstate(0, 0);
                for (i = 0; i < 10; i++)
                {
                    Logger::Write(L"%08X ", i >= 5 ? a3 : a2);
                    for (j = 0; j < 16; j += 2)
                    {
                        Logger::Write(L" %04X", get_word(i >= 5 ? a3 : a2));
                        if (i >= 5)
                            a3 += 2;
                        else
                            a2 += 2;
                    }
                    Logger::Write(L"\n");
                }
                memory_map_dump();
            }
            be_cnt++;
            if (be_cnt > 1000)
            {
                uae_reset(0);
                be_cnt = 0;
            }
            else
            {
                regs.panic = 1;
                regs.panic_pc = m68k_getpc();
                regs.panic_addr = a;
                set_special(SPCFLAG_BRK);
            }
        }
    }
    return kickmem_xlate(2);  /* So we don't crash. */
}

/* Address banks */

addrbank dummy_bank = {
    dummy_lget, dummy_wget, dummy_bget,
    dummy_lput, dummy_wput, dummy_bput,
    default_xlate, dummy_check, nullptr, nullptr,
    dummy_lgeti, dummy_wgeti, ABFLAG_NONE
};

addrbank ones_bank = {
    ones_get, ones_get, ones_get,
    none_put, none_put, none_put,
    default_xlate, dummy_check, nullptr, L"Ones",
    dummy_lgeti, dummy_wgeti, ABFLAG_NONE
};

addrbank chipmem_bank = {
    chipmem_lget, chipmem_wget, chipmem_bget,
    chipmem_lput, chipmem_wput, chipmem_bput,
    chipmem_xlate, chipmem_check, nullptr, L"Chip memory",
    chipmem_lget, chipmem_wget, ABFLAG_RAM
};

addrbank chipmem_dummy_bank = {
    chipmem_dummy_lget, chipmem_dummy_wget, chipmem_dummy_bget,
    chipmem_dummy_lput, chipmem_dummy_wput, chipmem_dummy_bput,
    default_xlate, dummy_check, nullptr, L"Dummy Chip memory",
    dummy_lgeti, dummy_wgeti, ABFLAG_IO
};

#ifdef AGA
addrbank chipmem_bank_ce2 = {
    chipmem_lget_ce2, chipmem_wget_ce2, chipmem_bget_ce2,
    chipmem_lput_ce2, chipmem_wput_ce2, chipmem_bput_ce2,
    chipmem_xlate, chipmem_check, nullptr, L"Chip memory (68020 'ce')",
    chipmem_lget_ce2, chipmem_wget_ce2, ABFLAG_RAM
};
#endif

addrbank slowmem_bank = {
    slowmem_lget, slowmem_wget, slowmem_bget,
    slowmem_lput, slowmem_wput, slowmem_bput,
    slowmem_xlate, slowmem_check, nullptr, L"Slow memory",
    slowmem_lget, slowmem_wget, ABFLAG_RAM
};

addrbank cardmem_bank = {
    cardmem_lget, cardmem_wget, cardmem_bget,
    cardmem_lput, cardmem_wput, cardmem_bput,
    cardmem_xlate, cardmem_check, nullptr, L"CDTV memory card",
    cardmem_lget, cardmem_wget, ABFLAG_RAM
};

addrbank a3000lmem_bank = {
    a3000lmem_lget, a3000lmem_wget, a3000lmem_bget,
    a3000lmem_lput, a3000lmem_wput, a3000lmem_bput,
    a3000lmem_xlate, a3000lmem_check, nullptr, L"RAMSEY memory (low)",
    a3000lmem_lget, a3000lmem_wget, ABFLAG_RAM
};

addrbank a3000hmem_bank = {
    a3000hmem_lget, a3000hmem_wget, a3000hmem_bget,
    a3000hmem_lput, a3000hmem_wput, a3000hmem_bput,
    a3000hmem_xlate, a3000hmem_check, nullptr, L"RAMSEY memory (high)",
    a3000hmem_lget, a3000hmem_wget, ABFLAG_RAM
};

addrbank kickmem_bank = {
    kickmem_lget, kickmem_wget, kickmem_bget,
    kickmem_lput, kickmem_wput, kickmem_bput,
    kickmem_xlate, kickmem_check, nullptr, L"Kickstart ROM",
    kickmem_lget, kickmem_wget, ABFLAG_ROM
};

addrbank kickram_bank = {
    kickmem_lget, kickmem_wget, kickmem_bget,
    kickmem2_lput, kickmem2_wput, kickmem2_bput,
    kickmem_xlate, kickmem_check, nullptr, L"Kickstart Shadow RAM",
    kickmem_lget, kickmem_wget, ABFLAG_UNK | ABFLAG_SAFE
};

addrbank extendedkickmem_bank = {
    extendedkickmem_lget, extendedkickmem_wget, extendedkickmem_bget,
    extendedkickmem_lput, extendedkickmem_wput, extendedkickmem_bput,
    extendedkickmem_xlate, extendedkickmem_check, nullptr, L"Extended Kickstart ROM",
    extendedkickmem_lget, extendedkickmem_wget, ABFLAG_ROM
};
addrbank extendedkickmem2_bank = {
    extendedkickmem2_lget, extendedkickmem2_wget, extendedkickmem2_bget,
    extendedkickmem2_lput, extendedkickmem2_wput, extendedkickmem2_bput,
    extendedkickmem2_xlate, extendedkickmem2_check, nullptr, L"Extended 2nd Kickstart ROM",
    extendedkickmem2_lget, extendedkickmem2_wget, ABFLAG_ROM
};

// static uint allocated_custmem1, allocated_custmem2;
// static uint custmem1_mask, custmem2_mask;
// static byte* custmem1, * custmem2;
//
// static uint REGPARAM3 custmem1_lget(uae_ptr) REGPARAM;
// static uint REGPARAM3 custmem1_wget(uae_ptr) REGPARAM;
// static uint REGPARAM3 custmem1_bget(uae_ptr) REGPARAM;
// static void REGPARAM3 custmem1_lput(uae_ptr, uint) REGPARAM;
// static void REGPARAM3 custmem1_wput(uae_ptr, uint) REGPARAM;
// static void REGPARAM3 custmem1_bput(uae_ptr, uint) REGPARAM;
// static int REGPARAM3 custmem1_check(uae_ptr addr, uint size) REGPARAM;
// static byte* REGPARAM3 custmem1_xlate(uae_ptr addr) REGPARAM;
//
// static uint REGPARAM2 custmem1_lget(uae_ptr addr)
// {
//     byte* m;
//     addr -= g_curr_conf.custom_memory_addrs[0] & custmem1_mask;
//     addr &= custmem1_mask;
//     m = custmem1 + addr;
//     return do_get_mem_long((uint*)m);
// }
// static uint REGPARAM2 custmem1_wget(uae_ptr addr)
// {
//     byte* m;
//     addr -= g_curr_conf.custom_memory_addrs[0] & custmem1_mask;
//     addr &= custmem1_mask;
//     m = custmem1 + addr;
//     return do_get_mem_word((ushort*)m);
// }
// static uint REGPARAM2 custmem1_bget(uae_ptr addr)
// {
//     addr -= g_curr_conf.custom_memory_addrs[0] & custmem1_mask;
//     addr &= custmem1_mask;
//     return custmem1[addr];
// }
// static void REGPARAM2 custmem1_lput(uae_ptr addr, uint l)
// {
//     byte* m;
//     addr -= g_curr_conf.custom_memory_addrs[0] & custmem1_mask;
//     addr &= custmem1_mask;
//     m = custmem1 + addr;
//     do_put_mem_long((uint*)m, l);
// }
// static void REGPARAM2 custmem1_wput(uae_ptr addr, uint w)
// {
//     byte* m;
//     addr -= g_curr_conf.custom_memory_addrs[0] & custmem1_mask;
//     addr &= custmem1_mask;
//     m = custmem1 + addr;
//     do_put_mem_word((ushort*)m, w);
// }
// static void REGPARAM2 custmem1_bput(uae_ptr addr, uint b)
// {
//     addr -= g_curr_conf.custom_memory_addrs[0] & custmem1_mask;
//     addr &= custmem1_mask;
//     custmem1[addr] = b;
// }
// static int REGPARAM2 custmem1_check(uae_ptr addr, uint size)
// {
//     addr -= g_curr_conf.custom_memory_addrs[0] & custmem1_mask;
//     addr &= custmem1_mask;
//     return (addr + size) <= g_curr_conf.custom_memory_sizes[0];
// }
// static byte* REGPARAM2 custmem1_xlate(uae_ptr addr)
// {
//     addr -= g_curr_conf.custom_memory_addrs[0] & custmem1_mask;
//     addr &= custmem1_mask;
//     return custmem1 + addr;
// }
//
// static uint REGPARAM3 custmem2_lget(uae_ptr) REGPARAM;
// static uint REGPARAM3 custmem2_wget(uae_ptr) REGPARAM;
// static uint REGPARAM3 custmem2_bget(uae_ptr) REGPARAM;
// static void REGPARAM3 custmem2_lput(uae_ptr, uint) REGPARAM;
// static void REGPARAM3 custmem2_wput(uae_ptr, uint) REGPARAM;
// static void REGPARAM3 custmem2_bput(uae_ptr, uint) REGPARAM;
// static int REGPARAM3 custmem2_check(uae_ptr addr, uint size) REGPARAM;
// static byte* REGPARAM3 custmem2_xlate(uae_ptr addr) REGPARAM;
//
// static uint REGPARAM2 custmem2_lget(uae_ptr addr)
// {
//     byte* m;
//     addr -= g_curr_conf.custom_memory_addrs[1] & custmem2_mask;
//     addr &= custmem2_mask;
//     m = custmem2 + addr;
//     return do_get_mem_long((uint*)m);
// }
// static uint REGPARAM2 custmem2_wget(uae_ptr addr)
// {
//     byte* m;
//     addr -= g_curr_conf.custom_memory_addrs[1] & custmem2_mask;
//     addr &= custmem2_mask;
//     m = custmem2 + addr;
//     return do_get_mem_word((ushort*)m);
// }
// static uint REGPARAM2 custmem2_bget(uae_ptr addr)
// {
//     addr -= g_curr_conf.custom_memory_addrs[1] & custmem2_mask;
//     addr &= custmem2_mask;
//     return custmem2[addr];
// }
// static void REGPARAM2 custmem2_lput(uae_ptr addr, uint l)
// {
//     byte* m;
//     addr -= g_curr_conf.custom_memory_addrs[1] & custmem2_mask;
//     addr &= custmem2_mask;
//     m = custmem2 + addr;
//     do_put_mem_long((uint*)m, l);
// }
// static void REGPARAM2 custmem2_wput(uae_ptr addr, uint w)
// {
//     byte* m;
//     addr -= g_curr_conf.custom_memory_addrs[1] & custmem2_mask;
//     addr &= custmem2_mask;
//     m = custmem2 + addr;
//     do_put_mem_word((ushort*)m, w);
// }
// static void REGPARAM2 custmem2_bput(uae_ptr addr, uint b)
// {
//     addr -= g_curr_conf.custom_memory_addrs[1] & custmem2_mask;
//     addr &= custmem2_mask;
//     custmem2[addr] = b;
// }
// static int REGPARAM2 custmem2_check(uae_ptr addr, uint size)
// {
//     addr -= g_curr_conf.custom_memory_addrs[1] & custmem2_mask;
//     addr &= custmem2_mask;
//     return (addr + size) <= g_curr_conf.custom_memory_sizes[1];
// }
// static byte* REGPARAM2 custmem2_xlate(uae_ptr addr)
// {
//     addr -= g_curr_conf.custom_memory_addrs[1] & custmem2_mask;
//     addr &= custmem2_mask;
//     return custmem2 + addr;
// }
//
// addrbank custmem1_bank = {
//     custmem1_lget, custmem1_wget, custmem1_bget,
//     custmem1_lput, custmem1_wput, custmem1_bput,
//     custmem1_xlate, custmem1_check, nullptr, L"Non-autoconfig RAM #1",
//     custmem1_lget, custmem1_wget, ABFLAG_RAM
// };
// addrbank custmem2_bank = {
//     custmem1_lget, custmem1_wget, custmem1_bget,
//     custmem1_lput, custmem1_wput, custmem1_bput,
//     custmem1_xlate, custmem1_check, nullptr, L"Non-autoconfig RAM #2",
//     custmem1_lget, custmem1_wget, ABFLAG_RAM
// };

#define fkickmem_size 524288
static int a3000_f0;
void a3000_fakekick(int map)
{
    static byte* kickstore;

    if (map)
    {
        byte* fkickmemory = a3000lmemory + allocated_a3000lmem - fkickmem_size;
        if (fkickmemory[2] == 0x4e && fkickmemory[3] == 0xf9 && fkickmemory[4] == 0x00)
        {
            if (!kickstore)
                kickstore = xmalloc(byte, fkickmem_size);
            memcpy(kickstore, kickmemory, fkickmem_size);
            if (fkickmemory[5] == 0xfc)
            {
                memcpy(kickmemory, fkickmemory, fkickmem_size / 2);
                memcpy(kickmemory + fkickmem_size / 2, fkickmemory, fkickmem_size / 2);
                extendedkickmem_size = 65536;
                extendedkickmem_mask = extendedkickmem_size - 1;
                extendedkickmemory = mapped_malloc(extendedkickmem_size, L"rom_f0");
                extendedkickmem_bank.baseaddr = extendedkickmemory;
                memcpy(extendedkickmemory, fkickmemory + fkickmem_size / 2, 65536);
                map_banks(&extendedkickmem_bank, 0xf0, 1, 1);
                a3000_f0 = 1;
            }
            else
            {
                memcpy(kickmemory, fkickmemory, fkickmem_size);
            }
        }
    }
    else
    {
        if (a3000_f0)
        {
            map_banks(&dummy_bank, 0xf0, 1, 1);
            mapped_free(extendedkickmemory);
            extendedkickmemory = nullptr;
            a3000_f0 = 0;
        }
        if (kickstore)
            memcpy(kickmemory, kickstore, fkickmem_size);
        free(kickstore);
        kickstore = nullptr;
    }
}

static char* kickstring = "exec.library";
static int read_kickstart(struct zfile* f, byte* mem, int size, int dochecksum, int noalias)
{
    char buffer[20];
    int i, j, oldpos;
    int /*cr = 0,*/ kickdisk = 0;

    if (size < 0)
    {
        zfile_fseek(f, 0, SEEK_END);
        size = zfile_ftell(f) & ~0x3ff;
        zfile_fseek(f, 0, SEEK_SET);
    }
    oldpos = zfile_ftell(f);
    i = zfile_fread(buffer, 1, 11, f);
    if (!memcmp(buffer, "KICK", 4))
    {
        zfile_fseek(f, 512, SEEK_SET);
        kickdisk = 1;
        //     #if 0
        // }
        // else if (size >= 524288 && !memcmp(buffer, "AMIG", 4))
        // {
        //     /* ReKick */
        //     zfile_fseek(f, oldpos + 0x6c, SEEK_SET);
        //     cr = 2;
        //     #endif
    }
    // else if (memcmp((char*)buffer, "AMIROMTYPE1", 11) != 0)
    // {
    zfile_fseek(f, oldpos, SEEK_SET);
    // }
    // else
    // {
    //     cloanto_rom = 1;
    //     cr = 1;
    // }

    memset(mem, 0, size);
    for (i = 0; i < 8; i++)
        mem[size - 16 + i * 2 + 1] = 0x18 + i;
    mem[size - 20] = size >> 24;
    mem[size - 19] = size >> 16;
    mem[size - 18] = size >> 8;
    mem[size - 17] = size >> 0;

    i = zfile_fread(mem, 1, size, f);

    if (kickdisk && i > 262144)
        i = 262144;
    #if 0
    if (i >= 262144 && (i != 262144 && i != 524288 && i != 524288 * 2 && i != 524288 * 4))
    {
        notify_user(NUMSG_KSROMREADERROR);
        return 0;
    }
    #endif
    if (i < size - 20)
        kickstart_fix_checksum(mem, size);

    j = 1;
    while (j < i)
        j <<= 1;
    i = j;

    if (!noalias && i == size / 2)
        memcpy(mem + size / 2, mem, size / 2);

    // if (cr)
    // {
    //     if (!decode_rom(mem, size, cr, i))
    //         return 0;
    // }
    if (g_curr_conf.cs_a1000ram)
    {
        int off = 0;
        a1000_bootrom = xcalloc(byte, 262144);
        while (off + i < 262144)
        {
            memcpy(a1000_bootrom + off, kickmemory, i);
            off += i;
        }
        memset(kickmemory, 0, kickmem_size);
        a1000_handle_kickstart(1);
        dochecksum = 0;
        i = 524288;
    }

    for (j = 0; j < 256 && i >= 262144; j++)
    {
        if (!memcmp(mem + j, kickstring, strlen(kickstring) + 1))
            break;
    }

    if (j == 256 || i < 262144)
        dochecksum = 0;
    if (dochecksum)
        kickstart_checksum(mem, size);
    return i;
}

static bool load_extendedkickstart(const TCHAR* romextfile, int type)
{
    struct zfile* f;
    int size, off;
    bool ret = false;

    if (_tcslen(romextfile) == 0)
        return false;
    // if (is_arcadia_rom(romextfile) == ARCADIA_BIOS)
    // {
    //     extendedkickmem_type = EXTENDED_ROM_ARCADIA;
    //     return false;
    // }
    f = read_rom_name(romextfile);
    if (!f)
    {
        notify_user(NUMSG_NOEXTROM);
        return false;
    }
    zfile_fseek(f, 0, SEEK_END);
    size = zfile_ftell(f);
    extendedkickmem_size = 524288;
    off = 0;
    if (type == 0)
    {
        if (g_curr_conf.cs_cd32cd)
        {
            extendedkickmem_type = EXTENDED_ROM_CD32;
        }
        else if (g_curr_conf.cs_cdtvcd || g_curr_conf.cs_cdtvram)
        {
            extendedkickmem_type = EXTENDED_ROM_CDTV;
        }
        else if (size > 300000)
        {
            extendedkickmem_type = EXTENDED_ROM_CD32;
        }
        else if (need_uae_boot_rom() != 0xf00000)
        {
            extendedkickmem_type = EXTENDED_ROM_CDTV;
        }
    }
    else
    {
        extendedkickmem_type = type;
    }
    if (extendedkickmem_type)
    {
        zfile_fseek(f, off, SEEK_SET);
        switch (extendedkickmem_type)
        {
            case EXTENDED_ROM_CDTV:
                extendedkickmemory = mapped_malloc(extendedkickmem_size, L"rom_f0");
                extendedkickmem_bank.baseaddr = extendedkickmemory;
                break;
            case EXTENDED_ROM_CD32:
                extendedkickmemory = mapped_malloc(extendedkickmem_size, L"rom_e0");
                extendedkickmem_bank.baseaddr = extendedkickmemory;
                break;
        }
        if (extendedkickmemory)
        {
            read_kickstart(f, extendedkickmemory, extendedkickmem_size, 0, 1);
            extendedkickmem_mask = extendedkickmem_size - 1;
            ret = true;
        }
    }
    zfile_fclose(f);
    return ret;
}

// static int patch_shapeshifter(byte* kickmemory)
// {
//     /* Patch Kickstart ROM for ShapeShifter - from Christian Bauer.
//      * Changes 'lea $400,a0' and 'lea $1000,a0' to 'lea $3000,a0' for
//      * ShapeShifter compatability.
//      */
//     int i, patched = 0;
//     byte kickshift1[] = { 0x41, 0xf8, 0x04, 0x00 };
//     byte kickshift2[] = { 0x41, 0xf8, 0x10, 0x00 };
//     byte kickshift3[] = { 0x43, 0xf8, 0x04, 0x00 };
//
//     for (i = 0x200; i < 0x300; i++)
//     {
//         if (!memcmp(kickmemory + i, kickshift1, sizeof(kickshift1)) ||
//             !memcmp(kickmemory + i, kickshift2, sizeof(kickshift2)) ||
//             !memcmp(kickmemory + i, kickshift3, sizeof(kickshift3)))
//         {
//             kickmemory[i + 2] = 0x30;
//             Logger::Write(L"Kickstart KickShifted @%04X\n", i);
//             patched++;
//         }
//     }
//     return patched;
// }

/* disable incompatible drivers */
static int patch_residents(byte* kickmemory, int size)
{
    int i, j, patched = 0;
    char* residents[] = { "NCR scsi.device", 0 };
    //  "scsi.device", "carddisk.device", "card.resource" };
    uae_ptr base = size == 524288 ? 0xf80000 : 0xfc0000;

    if (g_curr_conf.cs_mbdmac == 2)
        residents[0] = nullptr;
    for (i = 0; i < size - 100; i++)
    {
        if (kickmemory[i] == 0x4a && kickmemory[i + 1] == 0xfc)
        {
            uae_ptr addr;
            addr = (kickmemory[i + 2] << 24) | (kickmemory[i + 3] << 16) | (kickmemory[i + 4] << 8) | (kickmemory[i + 5] << 0);
            if (addr != i + base)
                continue;
            addr = (kickmemory[i + 14] << 24) | (kickmemory[i + 15] << 16) | (kickmemory[i + 16] << 8) | (kickmemory[i + 17] << 0);
            if (addr >= base && addr < base + size)
            {
                j = 0;
                while (residents[j])
                {
                    if (!memcmp(residents[j], kickmemory + addr - base, strlen(residents[j]) + 1))
                    {
                        TCHAR* s = Unicode::au(residents[j]);
                        Logger::Write(L"KSPatcher: '%s' at %08X disabled\n", s, i + base);
                        free(s);
                        kickmemory[i] = 0x4b; /* destroy RTC_MATCHWORD */
                        patched++;
                        break;
                    }
                    j++;
                }
            }
        }
    }
    return patched;
}

static void patch_kick()
{
    int patched = 0;
    // if (kickmem_size >= 524288 && g_curr_conf.kickshifter)
    //     patched += patch_shapeshifter(kickmemory);
    patched += patch_residents(kickmemory, kickmem_size);
    if (extendedkickmemory)
    {
        patched += patch_residents(extendedkickmemory, extendedkickmem_size);
        if (patched)
            kickstart_fix_checksum(extendedkickmemory, extendedkickmem_size);
    }
    if (patched)
        kickstart_fix_checksum(kickmemory, kickmem_size);
}

extern unsigned char arosrom[];
extern unsigned int arosrom_len;
// extern int seriallog;
static bool load_kickstart_replacement()
{
    struct zfile* f;

    f = zfile_fopen_data(L"aros.gz", arosrom_len, arosrom);
    if (!f)
        return false;
    f = zfile_gunzip(f);
    if (!f)
        return false;
    kickmem_mask = 0x80000 - 1;
    kickmem_size = 0x80000;
    extendedkickmem_size = 0x80000;
    extendedkickmem_type = EXTENDED_ROM_KS;
    extendedkickmemory = mapped_malloc(extendedkickmem_size, L"rom_e0");
    extendedkickmem_bank.baseaddr = extendedkickmemory;
    read_kickstart(f, extendedkickmemory, extendedkickmem_size, 0, 1);
    extendedkickmem_mask = extendedkickmem_size - 1;
    read_kickstart(f, kickmemory, 0x80000, 1, 0);
    zfile_fclose(f);

    Logging::SERIAL_DEBUG = -1;
    return true;
}

static int load_kickstart(void)
{
    struct zfile* f;
    TCHAR tmprom[MAX_DPATH], tmprom2[MAX_DPATH];
    // int patched = 0;

    // cloanto_rom = 0;
    if (!_tcscmp(g_curr_conf.romfile, L":AROS"))
        return load_kickstart_replacement();
    f = read_rom_name(g_curr_conf.romfile);
    _tcscpy(tmprom, g_curr_conf.romfile);
    if (f == nullptr)
    {
        _stprintf(tmprom2, L"%s%s", start_path_data, g_curr_conf.romfile);
        f = rom_fopen(tmprom2, L"rb", ZFD_NORMAL);
        if (f == nullptr)
        {
            g_curr_conf.romfile.Format(L"%sroms/kick.rom", start_path_data);
            f = rom_fopen(g_curr_conf.romfile, L"rb", ZFD_NORMAL);
            if (f == nullptr)
            {
                g_curr_conf.romfile.Format(L"%skick.rom", start_path_data);
                f = rom_fopen(g_curr_conf.romfile, L"rb", ZFD_NORMAL);
                if (f == nullptr)
                {
                    g_curr_conf.romfile.Format(L"%s../shared/rom/kick.rom", start_path_data);
                    f = rom_fopen(g_curr_conf.romfile, L"rb", ZFD_NORMAL);
                    if (f == nullptr)
                    {
                        g_curr_conf.romfile.Format(L"%s../System/rom/kick.rom", start_path_data);
                        f = rom_fopen(g_curr_conf.romfile, L"rb", ZFD_NORMAL);
                        if (f == nullptr)
                            f = read_rom_name_guess(tmprom);
                    }
                }
            }
        }
        else
        {
            g_curr_conf.romfile = tmprom2;
        }
    }
    // addkeydir(g_curr_conf.romfile);
    if (f == nullptr)   /* still no luck */
        goto err;

    // if (f != nullptr)
    // {
    int filesize, size, maxsize;
    int kspos = 524288;
    int extpos = 0;

    maxsize = 524288;
    zfile_fseek(f, 0, SEEK_END);
    filesize = zfile_ftell(f);
    zfile_fseek(f, 0, SEEK_SET);
    if (filesize == 1760 * 512)
    {
        filesize = 262144;
        maxsize = 262144;
    }
    if (filesize == 524288 + 8)
    {
        /* GVP 0xf0 kickstart */
        zfile_fseek(f, 8, SEEK_SET);
    }
    if (filesize >= 524288 * 2)
    {
        /*struct romdata* rd =*/ getromdatabyzfile(f);
        zfile_fseek(f, kspos, SEEK_SET);
    }
    if (filesize >= 524288 * 4)
    {
        kspos = 524288 * 3;
        extpos = 0;
        zfile_fseek(f, kspos, SEEK_SET);
    }
    size = read_kickstart(f, kickmemory, maxsize, 1, 0);
    if (size == 0)
        goto err;
    kickmem_mask = size - 1;
    kickmem_size = size;
    if (filesize >= 524288 * 2 && !extendedkickmem_type)
    {
        extendedkickmem_size = 0x80000;
        if (g_curr_conf.cs_cdtvcd || g_curr_conf.cs_cdtvram)
        {
            extendedkickmem_type = EXTENDED_ROM_CDTV;
            extendedkickmem_size *= 2;
            extendedkickmemory = mapped_malloc(extendedkickmem_size, L"rom_f0");
        }
        else
        {
            extendedkickmem_type = EXTENDED_ROM_KS;
            extendedkickmemory = mapped_malloc(extendedkickmem_size, L"rom_e0");
        }
        extendedkickmem_bank.baseaddr = extendedkickmemory;
        zfile_fseek(f, extpos, SEEK_SET);
        read_kickstart(f, extendedkickmemory, extendedkickmem_size, 0, 1);
        extendedkickmem_mask = extendedkickmem_size - 1;
    }
    if (filesize > 524288 * 2)
    {
        extendedkickmem2_size = 524288 * 2;
        extendedkickmemory2 = mapped_malloc(extendedkickmem2_size, L"rom_a8");
        extendedkickmem2_bank.baseaddr = extendedkickmemory2;
        zfile_fseek(f, extpos + 524288, SEEK_SET);
        read_kickstart(f, extendedkickmemory2, 524288, 0, 1);
        zfile_fseek(f, extpos + 524288 * 2, SEEK_SET);
        read_kickstart(f, extendedkickmemory2 + 524288, 524288, 0, 1);
        extendedkickmem2_mask = extendedkickmem2_size - 1;
    }
    // }

    #if defined (AMIGA)
chk_sum:
    #endif

    kickstart_version = (kickmemory[12] << 8) | kickmemory[13];
    if (kickstart_version == 0xffff)
        kickstart_version = 0;
    zfile_fclose(f);
    return 1;
err:
    g_curr_conf.romfile = tmprom;
    zfile_fclose(f);
    return 0;
}

// #ifndef NATMEM_OFFSET

byte* mapped_malloc(size_t s, const TCHAR* file)
{
    for (int i = 0; i < mapped_memory.GetSize(); i++)
    {
        if (mapped_memory[i]->name == file)
        {
            ASSERT(FALSE);
            return mapped_memory[i]->address;
        }
    }

    MemArea* ma = new MemArea();
    ma->address = new byte[s];
    ma->size = s;
    ma->name = file;

    mapped_memory.Add(ma);

    return ma->address;
}

void mapped_free(byte* p)
{
    if (p == nullptr)
        return;

    for (int i = 0; i < mapped_memory.GetSize(); i++)
    {
        if (mapped_memory[i]->address == p)
        {
            delete mapped_memory[i];
            mapped_memory.RemoveAt(i);
            return;
        }
    }

    ASSERT(FALSE);
}

// #else
//
// #include <mman.h>
//
// shmpiece* shm_start;
//
// static void dumplist()
// {
//     shmpiece* x = shm_start;
//     Logger::Write(L"Start Dump:\n");
//     while (x)
//     {
//         Logger::Write(L"this=%p,Native %p,id %d,prev=%p,next=%p,size=0x%08x\n",
//                   x, x->native_address, x->id, x->prev, x->next, x->size);
//         x = x->next;
//     }
//     Logger::Write(L"End Dump:\n");
// }
//
// static shmpiece * find_shmpiece(byte* base)
// {
//     shmpiece* x = shm_start;
//
//     while (x && x->native_address != base)
//         x = x->next;
//     if (!x)
//     {
//         Logger::Write(L"NATMEM: Failure to find mapping at %08X, %p\n", base - NATMEM_OFFSET, base);
//         nocanbang();
//         return 0;
//     }
//     return x;
// }
//
// static void delete_shmmaps(uint start, uint size)
// {
//     if (!canjit())
//         return;
//
//     while (size)
//     {
//         byte* base = mem_banks[bankindex(start)]->baseaddr;
//         if (base)
//         {
//             shmpiece* x;
//             //base = ((byte*)NATMEM_OFFSET)+start;
//
//             x = find_shmpiece(base);
//             if (!x)
//                 return;
//
//             if (x->size > size)
//             {
//                 if (isdirectjit())
//                     Logger::Write(L"NATMEM WARNING: size mismatch mapping at %08x (size %08x, delsize %08x)\n", start, x->size, size);
//                 size = x->size;
//             }
//             #if 0
//             dumplist();
//             nocanbang();
//             return;
//         }
//             #endif
//             shmdt(x->native_address);
//             size -= x->size;
//             start += x->size;
//             if (x->next)
//                 x->next->prev = x->prev; /* remove this one from the list */
//             if (x->prev)
//                 x->prev->next = x->next;
//             else
//                 shm_start = x->next;
//             free(x);
//         }
//         else
//         {
//             size -= 0x10000;
//             start += 0x10000;
//         }
//     }
// }
//
// static void add_shmmaps(uint start, addrbank* what)
// {
//     shmpiece* x = shm_start;
//     shmpiece* y;
//     byte* base = what->baseaddr;
//
//     if (!canjit())
//         return;
//     if (!base)
//         return;
//
//     x = find_shmpiece(base);
//     if (!x)
//         return;
//     y = xmalloc(shmpiece, 1);
//     *y = *x;
//     base = ((byte*)NATMEM_OFFSET) + start;
//     y->native_address = (byte*)shmat(y->id, base, 0);
//     if (y->native_address == (void*)-1)
//     {
//         Logger::Write(L"NATMEM: Failure to map existing at %08x (%p)\n", start, base);
//         dumplist();
//         nocanbang();
//         return;
//     }
//     y->next = shm_start;
//     y->prev = nullptr;
//     if (y->next)
//         y->next->prev = y;
//     shm_start = y;
// }
//
// byte * mapped_malloc(size_t s, const TCHAR* file)
// {
//     int id;
//     void* answer;
//     shmpiece* x;
//     static int recurse;
//
//     if (!canjit())
//     {
//         nocanbang();
//         return xcalloc(byte, s + 4);
//     }
//
//     id = shmget(IPC_PRIVATE, s, 0x1ff, file);
//     if (id == -1)
//     {
//         byte* p;
//         nocanbang();
//         if (recurse)
//             return nullptr;
//         recurse++;
//         p = mapped_malloc(s, file);
//         recurse--;
//         return p;
//     }
//     answer = shmat(id, 0, 0);
//     shmctl(id, IPC_RMID, nullptr);
//     if (answer != (void*)-1)
//     {
//         x = xmalloc(shmpiece, 1);
//         x->native_address = (byte*)answer;
//         x->id = id;
//         x->size = s;
//         x->next = shm_start;
//         x->prev = nullptr;
//         if (x->next)
//             x->next->prev = x;
//         shm_start = x;
//         return (byte*)answer;
//     }
//     if (recurse)
//         return nullptr;
//     nocanbang();
//     recurse++;
//     byte* r = mapped_malloc(s, file);
//     recurse--;
//     return r;
// }
//
// #endif

static void init_mem_banks()
{
    int i;

    for (i = 0; i < MEMORY_BANKS; i++)
        put_mem_bank(i << 16, &dummy_bank, 0);

    // #ifdef NATMEM_OFFSET
    // delete_shmmaps(0, 0xFFFF0000);
    // #endif
}

static void allocate_memory()
{
    // /* emulate 0.5M+0.5M with 1M Agnus chip ram aliasing */
    // if ((allocated_chipmem != g_curr_conf.chipmem_size || allocated_slowmem != g_curr_conf.slowmem_size) &&
    //     g_curr_conf.chipmem_size == 0x80000 && g_curr_conf.slowmem_size >= 0x80000 &&
    //     (g_curr_conf.chipset_mask & CSMASK_ECS_AGNUS) && !(g_curr_conf.chipset_mask & CSMASK_AGA) && !canjit())
    // {
    //     int memsize1, memsize2;
    //     if (chipmemory)
    //         mapped_free(chipmemory);
    //     chipmemory = 0;
    //     if (slowmemory_allocated)
    //         mapped_free(slowmemory);
    //     slowmemory = 0;
    //     slowmemory_allocated = 0;
    //     memsize1 = allocated_chipmem = g_curr_conf.chipmem_size;
    //     memsize2 = allocated_slowmem = g_curr_conf.slowmem_size;
    //     chipmem_mask = allocated_chipmem - 1;
    //     chipmem_full_mask = allocated_chipmem * 2 - 1;
    //     chipmem_full_size = 0x80000 * 2;
    //     chipmemory = mapped_malloc(memsize1 + memsize2, L"chip");
    //     slowmemory = chipmemory + memsize1;
    //     slowmem_mask = allocated_slowmem - 1;
    //     if (chipmemory == 0)
    //     {
    //         Logger::Write(L"Fatal error: out of memory for chipmem.\n");
    //         allocated_chipmem = 0;
    //     }
    //     else
    //     {
    //         need_hardreset = 1;
    //     }
    // }

    if (allocated_chipmem != g_curr_conf.chipmem_size)
    {
        int memsize;
        if (chipmemory)
            mapped_free(chipmemory);
        chipmemory = 0;
        // if (g_curr_conf.chipmem_size > 2 * 1024 * 1024)
        //     free_fastmemory();

        memsize = allocated_chipmem = chipmem_full_size = g_curr_conf.chipmem_size;
        chipmem_full_mask = chipmem_mask = allocated_chipmem - 1;
        if (memsize < 0x100000)
            memsize = 0x100000;
        if (memsize > 0x100000 && memsize < 0x200000)
            memsize = 0x200000;
        chipmemory = mapped_malloc(memsize, L"chip");
        // if (chipmemory == 0)
        // {
        //     Logger::Write(L"Fatal error: out of memory for chipmem.\n");
        //     allocated_chipmem = 0;
        // }
        // else
        // {
        //     need_hardreset = 1;
        if (memsize > allocated_chipmem)
            memset(chipmemory + allocated_chipmem, 0xff, memsize - allocated_chipmem);
        // }
        g_curr_conf.chipset_mask = g_changed_conf.chipset_mask;
        chipmem_full_mask = allocated_chipmem - 1;
        if (g_curr_conf.chipset_mask & CSMASK_ECS_AGNUS)
        {
            if (allocated_chipmem < 0x100000)
                chipmem_full_mask = 0x100000 - 1;
            if (allocated_chipmem > 0x100000 && allocated_chipmem < 0x200000)
                chipmem_full_mask = chipmem_mask = 0x200000 - 1;
        }
    }

    if (allocated_slowmem != g_curr_conf.slowmem_size)
    {
        if (slowmemory_allocated)
            mapped_free(slowmemory);
        slowmemory = 0;
        slowmemory_allocated = false;

        allocated_slowmem = g_curr_conf.slowmem_size;
        // if (allocated_slowmem >= 0x180000)
        //     allocated_slowmem = 0x200000;
        slowmem_mask = allocated_slowmem - 1;

        if (allocated_slowmem)
        {
            slowmemory = mapped_malloc(allocated_slowmem, L"slow");
            // if (slowmemory == 0)
            // {
            //     Logger::Write(L"Out of memory for slowmem.\n");
            slowmemory_allocated = true;
            // }
        }
        // need_hardreset = 1;
    }
    if (allocated_a3000lmem != g_curr_conf.mbresmem_low_size)
    {
        if (a3000lmemory)
            mapped_free(a3000lmemory);
        a3000lmemory = 0;

        allocated_a3000lmem = g_curr_conf.mbresmem_low_size;
        a3000lmem_mask = allocated_a3000lmem - 1;
        a3000lmem_start = 0x08000000 - allocated_a3000lmem;
        if (allocated_a3000lmem)
            // {
            a3000lmemory = mapped_malloc(allocated_a3000lmem, L"ramsey_low");
        //     if (a3000lmemory == 0)
        //     {
        //         Logger::Write(L"Out of memory for a3000lowmem.\n");
        //         allocated_a3000lmem = 0;
        //     }
        // }
        // need_hardreset = 1;
    }
    if (allocated_a3000hmem != g_curr_conf.mbresmem_high_size)
    {
        if (a3000hmemory)
            mapped_free(a3000hmemory);
        a3000hmemory = 0;

        allocated_a3000hmem = g_curr_conf.mbresmem_high_size;
        a3000hmem_mask = allocated_a3000hmem - 1;
        a3000hmem_start = 0x08000000;
        if (allocated_a3000hmem)
            // {
            a3000hmemory = mapped_malloc(allocated_a3000hmem, L"ramsey_high");
        //     if (a3000hmemory == 0)
        //     {
        //         Logger::Write(L"Out of memory for a3000highmem.\n");
        //         allocated_a3000hmem = 0;
        //     }
        // }
        // need_hardreset = 1;
    }
    if (allocated_cardmem != g_curr_conf.cs_cdtvcard * 1024)
    {
        if (cardmemory)
            mapped_free(cardmemory);
        cardmemory = 0;

        allocated_cardmem = g_curr_conf.cs_cdtvcard * 1024;
        cardmem_mask = allocated_cardmem - 1;
        if (allocated_cardmem)
            // {
            cardmemory = mapped_malloc(allocated_cardmem, L"rom_e0");
        //     if (cardmemory == 0)
        //     {
        //         Logger::Write(L"Out of memory for cardmem.\n");
        //         allocated_cardmem = 0;
        //     }
        // }
        cdtv_loadcardmem(cardmemory, allocated_cardmem);
    }

    // if (allocated_custmem1 != g_curr_conf.custom_memory_sizes[0])
    // {
    //     if (custmem1)
    //         mapped_free(custmem1);
    //     custmem1 = 0;
    //     allocated_custmem1 = g_curr_conf.custom_memory_sizes[0];
    //     custmem1_mask = allocated_custmem1 - 1;
    //     if (allocated_custmem1)
    //     {
    //         custmem1 = mapped_malloc(allocated_custmem1, L"custmem1");
    //         if (!custmem1)
    //             allocated_custmem1 = 0;
    //     }
    // }
    // if (allocated_custmem2 != g_curr_conf.custom_memory_sizes[1])
    // {
    //     if (custmem2)
    //         mapped_free(custmem2);
    //     custmem2 = 0;
    //     allocated_custmem2 = g_curr_conf.custom_memory_sizes[1];
    //     custmem2_mask = allocated_custmem2 - 1;
    //     if (allocated_custmem2)
    //     {
    //         custmem2 = mapped_malloc(allocated_custmem2, L"custmem2");
    //         if (!custmem2)
    //             allocated_custmem2 = 0;
    //     }
    // }

    if (savestate_state == STATE_RESTORE)
    {
        if (bootrom_filepos)
        {
            restore_ram(bootrom_filepos, rtarea);
        }
        restore_ram(chip_filepos, chipmemory);
        if (allocated_slowmem > 0)
            restore_ram(slow_filepos, slowmemory);
        if (allocated_a3000lmem > 0)
            restore_ram(a3000lmem_filepos, a3000lmemory);
        if (allocated_a3000hmem > 0)
            restore_ram(a3000hmem_filepos, a3000hmemory);
    }
    chipmem_bank.baseaddr = chipmemory;
    #ifdef AGA
    chipmem_bank_ce2.baseaddr = chipmemory;
    #endif
    slowmem_bank.baseaddr = slowmemory;
    a3000lmem_bank.baseaddr = a3000lmemory;
    a3000hmem_bank.baseaddr = a3000hmemory;
    cardmem_bank.baseaddr = cardmemory;
    bootrom_filepos = 0;
    chip_filepos = 0;
    slow_filepos = 0;
    a3000lmem_filepos = 0;
    a3000hmem_filepos = 0;
}

static void fill_ce_banks()
{
    int i;

    memset(ce_banktype, CE_MEMBANK_FAST, sizeof ce_banktype);
    //  data cachable regions
    memset(ce_cachable, 0, sizeof ce_cachable);
    memset(ce_cachable + (0x00200000 >> 16), 1, g_curr_conf.fastmem_size >> 16);
    memset(ce_cachable + (0x10000000 >> 16), 1, g_curr_conf.z3fastmem_size >> 16);

    if (&get_mem_bank(0) == &chipmem_bank)
    {
        for (i = 0; i < (0x200000 >> 16); i++)
            ce_banktype[i] = CE_MEMBANK_CHIP;
    }
    if (!g_curr_conf.cs_slowmemisfast)
    {
        for (i = (0xc00000 >> 16); i < (0xe00000 >> 16); i++)
            ce_banktype[i] = CE_MEMBANK_CHIP;
    }
    for (i = (0xd00000 >> 16); i < (0xe00000 >> 16); i++)
        ce_banktype[i] = CE_MEMBANK_CHIP;
    for (i = (0xa00000 >> 16); i < (0xc00000 >> 16); i++)
    {
        addrbank* b;
        ce_banktype[i] = CE_MEMBANK_CIA;
        b = &get_mem_bank(i << 16);
        if (b != &cia_bank)
        {
            ce_banktype[i] = CE_MEMBANK_FAST;
            ce_cachable[i] = 1;
        }
    }
    //  CD32 ROM is 16-bit
    if (g_curr_conf.cs_cd32cd)
    {
        for (i = (0xe00000 >> 16); i < (0xe80000 >> 16); i++)
            ce_banktype[i] = CE_MEMBANK_FAST16BIT;
        for (i = (0xf80000 >> 16); i <= (0xff0000 >> 16); i++)
            ce_banktype[i] = CE_MEMBANK_FAST16BIT;
    }
    if (g_curr_conf.address_space_24)
    {
        for (i = 1; i < 256; i++)
            memcpy(&ce_banktype[i * 256], &ce_banktype[0], 256);
    }
}

/*@
   CIAA OVR w amigach 600 i wyzej wisi w powietrzu, cala obluge overlay zajmuje sie gayle,
   w amigach przed 600 overlay mozna wlaczac i wylaczac w trakcie dzialania, w pozniejszych
   zgodnie z tym jak dziala winuae jakikolwiek wpis w obszar ciaa wylacza overlay, pytanie
   brzmi jaki to obszar pamieci, czy to rzeczywiscie tak dziala, czy czasem gayle nie ma
   jakis rejestrow na to,

   nastepne pytanie jaki obszar pamieci jest mapowany i w jaki, ile chipu zostaje, ile fast memu ?
   czy pozostaly obszar poza romem jest powielany czy tez sa tam zera

   z tego co pisze tutaj http://www.aep-emu.de/PNphpBB2-file-viewtopic-t-8152.html gayle ma mozliwosci
   tworzenia dodatkowych mirorrow romow. W zaleznosci od wersji amigi co jest pod adresami a8, e0, czy znowu
   tym mapowaniem mozna jakos sterowac.

   inne pytanie adresoanie chipa to 0,5mb, 1mb, 2mb, w zaleznosci od wersji chipa, czy wyzsze adresy to
   pustka, czy powielenie poprzednich.
 */

void map_overlay(int chip)
{
    int size;
    addrbank* cb;

    size = allocated_chipmem >= 0x180000 ? (allocated_chipmem >> 16) : 32;

    cb = &chipmem_bank;
    // #ifdef AGA
    // #if 0
    // if (g_curr_conf.cpu_cycle_exact && g_curr_conf.cpu_model >= 68020)
    //     cb = &chipmem_bank_ce2;
    // #endif
    // #endif
    if (chip)
    {
        map_banks(&dummy_bank, 0, 32, 0);
        // if (!isdirectjit())
        // {
        map_banks(cb, 0, size, allocated_chipmem);
        if ((g_curr_conf.chipset_mask & CSMASK_ECS_AGNUS) && allocated_slowmem == 0)
        {
            int start = allocated_chipmem >> 16;
            if (allocated_chipmem < 0x100000)
            {
                int dummy = (0x100000 - allocated_chipmem) >> 16;
                map_banks(&chipmem_dummy_bank, start, dummy, 0);
                map_banks(&chipmem_dummy_bank, start + 16, dummy, 0);
            }
            else if (allocated_chipmem < 0x200000 && allocated_chipmem > 0x100000)
            {
                int dummy = (0x200000 - allocated_chipmem) >> 16;
                map_banks(&chipmem_dummy_bank, start, dummy, 0);
            }
        }
        // }
        else
        {
            map_banks(cb, 0, allocated_chipmem >> 16, 0);
        }
    }
    else
    {
        /*@
            Along with the Kickstart ROM, this is the second area that is scanned for
            Resident modules. Furthermore, if the value $1111 is found in the 16-bit
            word at address $00F00000, control is turned over to the location directly
            following it (which is usually an absolute JMP, i.e. $4EF9). A similar
            technique is supported on Amigas equipped with a PCMCIA slot (e.g. the
            A600) to allow ROM cards to take over the entire Amiga very early during
            system start-up.
         */

        addrbank* rb = nullptr;
        if (size < 32)
            size = 32;
        cb = &get_mem_bank(0xf00000);
        if (!rb && cb && (cb->flags & ABFLAG_ROM) && get_word(0xf00000) == 0x1114)
            rb = cb;
        cb = &get_mem_bank(0xe00000);
        if (!rb && cb && (cb->flags & ABFLAG_ROM) && get_word(0xe00000) == 0x1114)
            rb = cb;
        if (!rb)
            rb = &kickmem_bank;

        map_banks(rb, 0, size, 0x80000);
    }

    fill_ce_banks();
    if (!isrestore() && valid_address(regs.pc, 4))
        m68k_setpc(m68k_getpc());
}

void memory_reset()
{
    int bnk, bnk_end;
    int gayle;

    be_cnt = 0;
    g_curr_conf.chipmem_size = g_changed_conf.chipmem_size;
    g_curr_conf.slowmem_size = g_changed_conf.slowmem_size;
    g_curr_conf.mbresmem_low_size = g_changed_conf.mbresmem_low_size;
    g_curr_conf.mbresmem_high_size = g_changed_conf.mbresmem_high_size;
    g_curr_conf.cs_ksmirror_e0 = g_changed_conf.cs_ksmirror_e0;
    g_curr_conf.cs_ksmirror_a8 = g_changed_conf.cs_ksmirror_a8;
    g_curr_conf.cs_ciaoverlay = g_changed_conf.cs_ciaoverlay;
    g_curr_conf.cs_cdtvram = g_changed_conf.cs_cdtvram;
    g_curr_conf.cs_cdtvcard = g_changed_conf.cs_cdtvcard;
    g_curr_conf.cs_a1000ram = g_changed_conf.cs_a1000ram;
    g_curr_conf.cs_ide = g_changed_conf.cs_ide;
    g_curr_conf.cs_fatgaryrev = g_changed_conf.cs_fatgaryrev;
    g_curr_conf.cs_ramseyrev = g_changed_conf.cs_ramseyrev;

    gayle = (g_curr_conf.chipset_mask & CSMASK_AGA) || g_curr_conf.cs_pcmcia || g_curr_conf.cs_ide > 0;

    // need_hardreset = 0;
    // /* Use g_changed_conf, as m68k_reset is called later.  */
    // if (last_address_space_24 != g_changed_conf.address_space_24)
    //     need_hardreset = 1;

    last_address_space_24 = g_changed_conf.address_space_24;

    init_mem_banks();
    allocate_memory();
    chipmem_setindirect();

    if (_tcscmp(g_curr_conf.romfile, g_changed_conf.romfile) != 0
        || _tcscmp(g_curr_conf.romextfile, g_changed_conf.romextfile) != 0)
    {
        if (Logging::MEMORY_DEBUG)
            Logger::Write(L"ROM loader.. (%s)\n", g_curr_conf.romfile);

        kickstart_rom = 1;
        a1000_handle_kickstart(0);
        free(a1000_bootrom);
        a1000_bootrom = 0;
        a1000_kickstart_mode = 0;

        g_curr_conf.romfile = g_changed_conf.romfile;
        g_curr_conf.romextfile = g_changed_conf.romextfile;
        // need_hardreset = 1;
        mapped_free(extendedkickmemory);
        extendedkickmemory = 0;
        extendedkickmem_size = 0;
        extendedkickmemory2 = 0;
        extendedkickmem2_size = 0;
        extendedkickmem_type = 0;
        load_extendedkickstart(g_curr_conf.romextfile, 0);
        // load_extendedkickstart(g_curr_conf.romextfile2, EXTENDED_ROM_CDTV);
        kickmem_mask = 524288 - 1;
        if (!load_kickstart())
        {
            if (_tcslen(g_curr_conf.romfile) > 0)
            {
                Logger::Write(L"Failed to open '%s'\n", g_curr_conf.romfile);
                notify_user(NUMSG_NOROM);
            }
            load_kickstart_replacement();
        }
        else
        {
            struct romdata* rd = getromdatabydata(kickmemory, kickmem_size);

            if (rd)
            {
                Logger::Write(L"Known ROM '%s' loaded\n", rd->name);

                if ((rd->cpu & 3) == 3 && g_changed_conf.cpu_model != 68030)
                {
                    notify_user(NUMSG_KS68030);
                    uae_restart(UaeRestart_DisableNoGui, nullptr);
                }
                else if ((rd->cpu & 3) == 1 && g_changed_conf.cpu_model < 68020)
                {
                    notify_user(NUMSG_KS68EC020);
                    uae_restart(UaeRestart_DisableNoGui, nullptr);
                }
                else if ((rd->cpu & 3) == 2 && (g_changed_conf.cpu_model < 68020 || g_changed_conf.address_space_24))
                {
                    notify_user(NUMSG_KS68020);
                    uae_restart(UaeRestart_DisableNoGui, nullptr);
                }
                // if (rd->cloanto)
                //     cloanto_rom = 1;

                kickstart_rom = 0;

                if ((rd->type & ROMTYPE_SPECIALKICK | ROMTYPE_KICK) == ROMTYPE_KICK)
                    kickstart_rom = 1;

                //  Bez tego amiga z romem z a4000 ale bez ide4000 uruchamia sie cholernie dlugo.
                if ((rd->cpu & 4) && g_curr_conf.cs_compatible)
                {
                    /* A4000 ROM = need ramsey, gary and ide */
                    if (g_curr_conf.cs_ramseyrev < 0)
                        g_changed_conf.cs_ramseyrev = g_curr_conf.cs_ramseyrev = 0x0f;
                    g_changed_conf.cs_fatgaryrev = g_curr_conf.cs_fatgaryrev = 0;
                    if (g_curr_conf.cs_ide != IDE_A4000)
                        g_changed_conf.cs_ide = g_curr_conf.cs_ide = -1;
                }
            }
            else
            {
                Logger::Write(L"Unknown ROM '%s' loaded\n", g_curr_conf.romfile);
            }
        }
        patch_kick();

        if (Logging::MEMORY_DEBUG)
            Logger::Write(L"ROM loader end\n");
    }

    // if (cloanto_rom && g_curr_conf.maprom < 0x01000000)
    //     g_curr_conf.maprom = g_changed_conf.maprom = 0;

    gayle = g_curr_conf.cs_ksmirror_a8 || g_curr_conf.cs_pcmcia || g_curr_conf.cs_ide > 0;

    map_banks(&custom_bank, 0xC0, 0xE0 - 0xC0, 0);
    map_banks(&cia_bank, 0xA0, 32, 0);
    if (!g_curr_conf.cs_a1000ram)
        /* D80000 - DDFFFF not mapped (A1000 = custom chips) */
        map_banks(&dummy_bank, 0xD8, 6, 0);

    /* map "nothing" to 0x200000 - 0x9FFFFF (0xBEFFFF if Gayle) */
    bnk = allocated_chipmem >> 16;
    if (bnk < 0x20 + (g_curr_conf.fastmem_size >> 16))
        bnk = 0x20 + (g_curr_conf.fastmem_size >> 16);
    bnk_end = gayle ? 0xBF : 0xA0;
    map_banks(&dummy_bank, bnk, bnk_end - bnk, 0);
    if (gayle)
        map_banks(&dummy_bank, 0xc0, 0xd8 - 0xc0, 0);

    if (slowmemory != 0)
    {
        // int t = g_curr_conf.slowmem_size >> 16;
        // if (t > 0x1C)
        //     t = 0x1C;
        // if (t > 0x18 && ((g_curr_conf.chipset_mask & CSMASK_AGA) || (g_curr_conf.cpu_model >= 68020 && !g_curr_conf.address_space_24)))
        //     t = 0x18;
        map_banks(&slowmem_bank, 0xC0, g_curr_conf.slowmem_size >> 16, 0);
    }

    if (g_curr_conf.cs_ide || g_curr_conf.cs_pcmcia)
    {
        if (g_curr_conf.cs_ide == IDE_A600A1200 || g_curr_conf.cs_pcmcia)
        {
            map_banks(&gayle_bank, 0xD8, 6, 0);
            map_banks(&gayle2_bank, 0xDD, 2, 0);
        }
        gayle_map_pcmcia();
        if (g_curr_conf.cs_ide == IDE_A4000 || g_curr_conf.cs_mbdmac == 2)
            map_banks(&gayle_bank, 0xDD, 1, 0);
        if (g_curr_conf.cs_ide < 0 && !g_curr_conf.cs_pcmcia)
            map_banks(&gayle_bank, 0xD8, 6, 0);
        if (g_curr_conf.cs_ide < 0)
            map_banks(&gayle_bank, 0xDD, 1, 0);
    }
    if (g_curr_conf.cs_rtc || g_curr_conf.cs_cdtvram)
        map_banks(&clock_bank, 0xDC, 1, 0);
    else if (g_curr_conf.cs_ksmirror_a8 || g_curr_conf.cs_ide > 0 || g_curr_conf.cs_pcmcia)
        map_banks(&clock_bank, 0xDC, 1, 0);  /* none clock */
    if (g_curr_conf.cs_fatgaryrev >= 0 || g_curr_conf.cs_ramseyrev >= 0)
        map_banks(&mbres_bank, 0xDE, 1, 0);
    if (g_curr_conf.cs_cd32c2p || g_curr_conf.cs_cd32cd || g_curr_conf.cs_cd32nvram)
    {
        map_banks(&akiko_bank, AKIKO_BASE >> 16, 1, 0);
        map_banks(&gayle2_bank, 0xDD, 2, 0);
    }
    if (g_curr_conf.cs_cdtvcd)
        cdtv_check_banks();
    if (g_curr_conf.cs_mbdmac == 1)
        a3000scsi_reset();

    if (a3000lmemory != 0)
        map_banks(&a3000lmem_bank, a3000lmem_start >> 16, allocated_a3000lmem >> 16, 0);
    if (a3000hmemory != 0)
        map_banks(&a3000hmem_bank, a3000hmem_start >> 16, allocated_a3000hmem >> 16, 0);
    if (cardmemory != 0)
        map_banks(&cardmem_bank, cardmem_start >> 16, allocated_cardmem >> 16, 0);

    map_banks(&kickmem_bank, 0xF8, 8, 0);

    // if (g_curr_conf.maprom)
    //     map_banks(&kickram_bank, g_curr_conf.maprom >> 16, 8, 0);
    // /* map beta Kickstarts at 0x200000/0xC00000/0xF00000 */
    // if (kickmemory[0] == 0x11 && kickmemory[2] == 0x4e && kickmemory[3] == 0xf9 && kickmemory[4] == 0x00)
    // {
    //     uint addr = kickmemory[5];
    //     if (addr == 0x20 && g_curr_conf.chipmem_size <= 0x200000 && g_curr_conf.fastmem_size == 0)
    //         map_banks(&kickmem_bank, addr, 8, 0);
    //     if (addr == 0xC0 && g_curr_conf.slowmem_size == 0)
    //         map_banks(&kickmem_bank, addr, 8, 0);
    //     if (addr == 0xF0)
    //         map_banks(&kickmem_bank, addr, 8, 0);
    // }

    if (a1000_bootrom)
        a1000_handle_kickstart(1);

    #ifdef AUTOCONFIG
    map_banks(&expamem_bank, 0xE8, 1, 0);
    #endif

    if (a3000_f0)
        map_banks(&extendedkickmem_bank, 0xf0, 1, 0);

    /* Map the chipmem into all of the lower 8MB */
    map_overlay(1);

    switch (extendedkickmem_type)
    {
        case EXTENDED_ROM_KS:
            map_banks(&extendedkickmem_bank, 0xE0, 8, 0);
            break;
            #ifdef CDTV
        case EXTENDED_ROM_CDTV:
            map_banks(&extendedkickmem_bank, 0xF0, extendedkickmem_size == 2 * 524288 ? 16 : 8, 0);
            break;
            #endif
            #ifdef CD32
        case EXTENDED_ROM_CD32:
            map_banks(&extendedkickmem_bank, 0xE0, 8, 0);
            break;
            #endif
    }

    #ifdef AUTOCONFIG
    if (need_uae_boot_rom())
        map_banks(&rtarea_bank, rtarea_base >> 16, 1, 0);
    #endif

    if ((g_curr_conf.cs_ksmirror_e0) && !extendedkickmem_type)
        map_banks(&kickmem_bank, 0xE0, 8, 0);
    if (g_curr_conf.cs_ksmirror_a8)
    {
        if (extendedkickmem2_size)
        {
            map_banks(&extendedkickmem2_bank, 0xa8, 16, 0);
        }
        else
        {
            struct romdata* rd = getromdatabypath(g_curr_conf.cartfile);
            if (!rd || rd->id != 63)
            {
                if (extendedkickmem_type == EXTENDED_ROM_CD32 || extendedkickmem_type == EXTENDED_ROM_KS)
                    map_banks(&extendedkickmem_bank, 0xb0, 8, 0);
                else
                    map_banks(&kickmem_bank, 0xb0, 8, 0);
                map_banks(&kickmem_bank, 0xa8, 8, 0);
            }
        }
    }

    // if (g_curr_conf.custom_memory_sizes[0])
    // {
    //     map_banks(&custmem1_bank,
    //               g_curr_conf.custom_memory_addrs[0] >> 16,
    //               g_curr_conf.custom_memory_sizes[0] >> 16, 0);
    // }
    // if (g_curr_conf.custom_memory_sizes[1])
    // {
    //     map_banks(&custmem2_bank,
    //               g_curr_conf.custom_memory_addrs[1] >> 16,
    //               g_curr_conf.custom_memory_sizes[1] >> 16, 0);
    // }

    // #ifdef ARCADIA
    // if (is_arcadia_rom(g_curr_conf.romextfile) == ARCADIA_BIOS)
    // {
    //     if (_tcscmp(g_curr_conf.romextfile, g_changed_conf.romextfile) != 0)
    //         memcpy(g_curr_conf.romextfile, g_changed_conf.romextfile, sizeof g_curr_conf.romextfile);
    //     if (_tcscmp(g_curr_conf.cartfile, g_changed_conf.cartfile) != 0)
    //         memcpy(g_curr_conf.cartfile, g_changed_conf.cartfile, sizeof g_curr_conf.cartfile);
    //     arcadia_unmap();
    //     is_arcadia_rom(g_curr_conf.romextfile);
    //     is_arcadia_rom(g_curr_conf.cartfile);
    //     arcadia_map_banks();
    // }
    // #endif

    #ifdef ACTION_REPLAY
    // #ifdef ARCADIA
    // if (!arcadia_bios)
    // {
    // #endif
    action_replay_memory_reset();
    // //#ifdef ARCADIA
    // }
    // #endif
    #endif

    if (Logging::MEMORY_DEBUG)
        Logger::Write(L"memory init end\n");
}

void memory_init()
{
    allocated_chipmem = 0;
    allocated_slowmem = 0;
    kickmemory = 0;
    extendedkickmemory = 0;
    extendedkickmem_size = 0;
    extendedkickmemory2 = 0;
    extendedkickmem2_size = 0;
    extendedkickmem_type = 0;
    chipmemory = 0;
    allocated_a3000lmem = allocated_a3000hmem = 0;
    a3000lmemory = a3000hmemory = 0;
    slowmemory = 0;
    cardmemory = 0;
    // custmem1 = 0;
    // custmem2 = 0;

    init_mem_banks();

    kickmemory = mapped_malloc(0x80000, L"kick");
    memset(kickmemory, 0, 0x80000);
    kickmem_bank.baseaddr = kickmemory;
    g_curr_conf.romfile = L"<none>";
    g_curr_conf.romextfile = "";

    #ifdef ACTION_REPLAY
    action_replay_load();
    action_replay_init(1);
    #ifdef ACTION_REPLAY_HRTMON
    hrtmon_load();
    #endif
    #endif
}

void memory_cleanup()
{
    if (a3000lmemory)
        mapped_free(a3000lmemory);

    if (a3000hmemory)
        mapped_free(a3000hmemory);

    if (slowmemory_allocated)
        mapped_free(slowmemory);
    slowmemory_allocated = false;

    if (kickmemory)
        mapped_free(kickmemory);

    if (a1000_bootrom)
        free(a1000_bootrom);

    if (chipmemory)
        mapped_free(chipmemory);

    if (cardmemory)
    {
        cdtv_savecardmem(cardmemory, allocated_cardmem);
        mapped_free(cardmemory);
    }

    // if (custmem1)
    //     mapped_free(custmem1);
    // if (custmem2)
    //     mapped_free(custmem2);

    mapped_free(rtarea);
    rtarea = nullptr;

    mapped_free(extendedkickmemory);
    extendedkickmemory = nullptr;
    slowmemory = 0;
    kickmemory = 0;
    a3000lmemory = a3000hmemory = 0;
    a1000_bootrom = 0;
    a1000_kickstart_mode = 0;
    chipmemory = 0;
    // custmem1 = 0;
    // custmem2 = 0;

    #ifdef ACTION_REPLAY
    action_replay_cleanup();
    #endif
    // #ifdef ARCADIA
    // arcadia_unmap();
    // #endif
}

void memory_hardreset()
{
    if (savestate_state == STATE_RESTORE)
        return;
    if (chipmemory)
        memset(chipmemory, 0, allocated_chipmem);
    if (slowmemory)
        memset(slowmemory, 0, allocated_slowmem);
    if (a3000lmemory)
        memset(a3000lmemory, 0, allocated_a3000lmem);
    if (a3000hmemory)
        memset(a3000hmemory, 0, allocated_a3000hmem);

    expansion_clear();
}

void map_banks(addrbank* bank, int start, int size, int realsize)
{
    int bnr, old;
    uint hioffs = 0, endhioffs = 0x100;
    // addrbank* orgbank = bank;
    uint realstart = start;

    // Logger::Write (L"MAP_BANK %04X0000 %d %s\n", start, size, bank->name);

    old = debug_bankchange(-1);
    flush_icache(0, 3);  /* Sure don't want to keep any old mappings around! */

    // #ifdef NATMEM_OFFSET
    // delete_shmmaps(start << 16, size << 16);
    // #endif

    if (!realsize)
        realsize = size << 16;

    if ((size << 16) < realsize)
    {
        Logger::Write(L"Broken mapping, size=%x, realsize=%x\nStart is %x\n",
            size, realsize, start);
    }

    #ifndef ADDRESS_SPACE_24BIT
    if (start >= 0x100)
    {
        int real_left = 0;
        for (bnr = start; bnr < start + size; bnr++)
        {
            if (!real_left)
            {
                realstart = bnr;
                real_left = realsize >> 16;

                // #ifdef NATMEM_OFFSET
                // add_shmmaps(realstart << 16, bank);
                // #endif
            }
            put_mem_bank(bnr << 16, bank, realstart << 16);
            real_left--;
        }
        debug_bankchange(old);
        return;
    }
    #endif
    if (last_address_space_24)
        endhioffs = 0x10000;
    #ifdef ADDRESS_SPACE_24BIT
    endhioffs = 0x100;
    #endif
    for (hioffs = 0; hioffs < endhioffs; hioffs += 0x100)
    {
        int real_left = 0;
        for (bnr = start; bnr < start + size; bnr++)
        {
            if (!real_left)
            {
                realstart = bnr + hioffs;
                real_left = realsize >> 16;

                // #ifdef NATMEM_OFFSET
                // add_shmmaps(realstart << 16, bank);
                // #endif
            }
            put_mem_bank((bnr + hioffs) << 16, bank, realstart << 16);
            real_left--;
        }
    }
    debug_bankchange(old);
    fill_ce_banks();
}

#ifdef SAVESTATE

/* memory save/restore code */

byte* save_bootrom(int* len)
{
    if (!uae_boot_rom)
        return 0;
    *len = uae_boot_rom_size;
    return rtarea;
}

byte* save_cram(int* len)
{
    *len = allocated_chipmem;
    return chipmemory;
}

byte* save_sram(int* len)
{
    *len = allocated_slowmem;
    return slowmemory;
}

byte* save_a3000lram(int* len)
{
    *len = allocated_a3000lmem;
    return a3000lmemory;
}

byte* save_a3000hram(int* len)
{
    *len = allocated_a3000hmem;
    return a3000hmemory;
}

void restore_bootrom(int len, size_t filepos)
{
    bootrom_filepos = filepos;
}

void restore_cram(int len, size_t filepos)
{
    chip_filepos = filepos;
    g_changed_conf.chipmem_size = len;
}

void restore_sram(int len, size_t filepos)
{
    slow_filepos = filepos;
    g_changed_conf.slowmem_size = len;
}

void restore_a3000lram(int len, size_t filepos)
{
    a3000lmem_filepos = filepos;
    g_changed_conf.mbresmem_low_size = len;
}

void restore_a3000hram(int len, size_t filepos)
{
    a3000hmem_filepos = filepos;
    g_changed_conf.mbresmem_high_size = len;
}

byte* restore_rom(byte* src)
{
    uint crc32, mem_start, mem_size, mem_type, version;
    TCHAR* s, * romn;
    int crcdet;

    mem_start = restore_u32();
    mem_size = restore_u32();
    mem_type = restore_u32();
    version = restore_u32();
    crc32 = restore_u32();
    romn = restore_string();
    crcdet = 0;

    if (crc32)
    {
        auto rl = get_romlist_by_crc(crc32);

        if (rl)
        {
            if (zfile_exists(rl->path))
            {
                switch (mem_type)
                {
                    case 0:
                        g_changed_conf.romfile = rl->path;
                        break;
                    case 1:
                        g_changed_conf.romextfile = rl->path;
                        break;
                }
                Logger::Write(L"ROM '%s' = '%s'\n", romn, rl->path);
                crcdet = 1;
            }
            else
            {
                Logger::Write(L"ROM '%s' = '%s' invalid rom scanner path!", romn, rl->path);
            }
        }
    }

    s = restore_string();
    if (!crcdet)
    {
        if (zfile_exists(s))
        {
            switch (mem_type)
            {
                case 0:
                    g_changed_conf.romfile = s;
                    break;
                case 1:
                    g_changed_conf.romextfile = s;
                    break;
            }
            Logger::Write(L"ROM detected (path) as '%s'\n", s);
            crcdet = 1;
        }
    }
    free(s);
    if (!crcdet)
        Logger::Write(L"WARNING: ROM '%s' not found!\n", romn);
    free(romn);
    return src;
}

byte* save_rom(int first, int* len, byte* dstptr)
{
    static int count;
    byte* dst, * dstbak;
    byte* mem_real_start = 0;
    uint version;
    const TCHAR* path = 0;
    int mem_start = 0, mem_size, mem_type, saverom;
    int i;
    TCHAR tmpname[1000];

    version = 0;
    saverom = 0;
    if (first)
        count = 0;
    for (;;)
    {
        mem_type = count;
        mem_size = 0;
        switch (count)
        {
            case 0: /* Kickstart ROM */
                mem_start = 0xf80000;
                mem_real_start = kickmemory;
                mem_size = kickmem_size;
                path = g_curr_conf.romfile;
                /* 256KB or 512KB ROM? */
                for (i = 0; i < mem_size / 2 - 4; i++)
                {
                    if (longget(i + mem_start) != longget(i + mem_start + mem_size / 2))
                        break;
                }
                if (i == mem_size / 2 - 4)
                {
                    mem_size /= 2;
                    mem_start += 262144;
                }
                version = longget(mem_start + 12); /* version+revision */
                _stprintf(tmpname, L"Kickstart %d.%d", wordget(mem_start + 12), wordget(mem_start + 14));
                break;
            case 1: /* Extended ROM */
                if (!extendedkickmem_type)
                    break;
                mem_start = extendedkickmem_start;
                mem_real_start = extendedkickmemory;
                mem_size = extendedkickmem_size;
                path = g_curr_conf.romextfile;
                _stprintf(tmpname, L"Extended");
                break;
            default:
                return 0;
        }
        count++;
        if (mem_size)
            break;
    }
    if (dstptr)
        dstbak = dst = dstptr;
    else
        dstbak = dst = xmalloc(byte, 4 + 4 + 4 + 4 + 4 + 256 + 256 + mem_size);
    save_u32(mem_start);
    save_u32(mem_size);
    save_u32(mem_type);
    save_u32(version);
    save_u32(get_crc32(mem_real_start, mem_size));
    save_string(tmpname);
    save_string(path);
    if (saverom)
    {
        for (i = 0; i < mem_size; i++)
            *dst++ = byteget(mem_start + i);
    }
    *len = dst - dstbak;
    return dstbak;
}

#endif /* SAVESTATE */

/* memory helpers */

void memcpyha_safe(uae_ptr dst, const byte* src, int size)
{
    if (!addr_valid(L"memcpyha", dst, size))
        return;
    while (size--)
        put_byte(dst++, *src++);
}
void memcpyha(uae_ptr dst, const byte* src, int size)
{
    while (size--)
        put_byte(dst++, *src++);
}
void memcpyah_safe(byte* dst, uae_ptr src, int size)
{
    if (!addr_valid(L"memcpyah", src, size))
        return;
    while (size--)
        *dst++ = get_byte(src++);
}
void memcpyah(byte* dst, uae_ptr src, int size)
{
    while (size--)
        *dst++ = get_byte(src++);
}
char* strcpyah_safe(char* dst, uae_ptr src, int maxsize)
{
    char* res = dst;
    byte b;
    do
    {
        if (!addr_valid(L"_tcscpyah", src, 1))
            return res;
        b = get_byte(src++);
        *dst++ = b;
        maxsize--;
        if (maxsize <= 1)
        {
            *dst++ = 0;
            break;
        }
    }
    while (b);
    return res;
}
uae_ptr strcpyha_safe(uae_ptr dst, const char* src)
{
    uae_ptr res = dst;
    byte b;
    do
    {
        if (!addr_valid(L"_tcscpyha", dst, 1))
            return res;
        b = *src++;
        put_byte(dst++, b);
    }
    while (b);
    return res;
}